#scala #scala-macros #scala-3
Вопрос:
Моя задача-распечатать информацию о типе в нотации, подобной Java (используя <
, >
для обозначения аргументов типа). В scala 2 у меня есть этот небольшой метод, используемый scala.reflect.Manifest
в качестве источника для символа типа и его параметров:
def typeOf[T](implicit manifest: Manifest[T]): String = {
def loop[T0](m: Manifest[T0]): String =
if (m.typeArguments.isEmpty) m.runtimeClass.getSimpleName
else {
val typeArguments = m.typeArguments.map(loop(_)).mkString(",")
raw"""${m.runtimeClass.getSimpleName}<$typeArguments>"""
}
loop(manifest)
}
К сожалению, в Scala 3 Манифесты недоступны. Есть ли собственный способ переписать это с помощью Scala 3? Я открыт для некоторых встроенных макросов. То, что я пробовал до сих пор, — это
inline def typeOf[T]: String = ${typeOfImpl}
private def typeOfImpl[T: Type](using Quotes): Expr[String] =
import quotes.reflect.*
val tree = TypeTree.of[T]
tree.show
// ^^ call is parameterized with Printer but AFAIK there's no way
// to provide your own implementation for it. You can to chose
// from predefined ones. So how do I proceed from here?
Я знаю, что типы Scala не могут быть полностью представлены в виде типов Java. Я стремлюсь охватить только простые, которые смог охватить оригинальный метод. Никаких подстановочных знаков или экзистенциалов, только полностью разрешенные типы, такие как:
List[String]
RES:List<String>
List[Option[String]]
RES:List<Option<String>>
Map[String,Option[Int]]
RES:Map<String,Option<Int>>
Ответ №1:
Я публикую этот ответ, хотя это не окончательное решение, и, возможно, есть лучший способ, но, надеюсь, он может дать вам некоторые идеи.
Я думаю, что хорошим началом является использование TypeRepr
:
val tpr: TypeRepr = TypeRepr.of[T]
val typeParams: List[TypeRepr] = tpr match {
case a: AppliedType => a.args
case _ => Nil
}
Тогда с помощью рекурсивного метода вы сможете что-то придумать.
Скопировано с вдохновенного из https://github.com/gaeljw/typetrees/blob/main/src/main/scala/io/github/gaeljw/typetrees/TypeTreeTagMacros.scala#L12:
private def getTypeString[T](using Type[T], Quotes): Expr[String] = {
import quotes.reflect._
def getTypeStringRec(tpr: TypeRepr)(using Quotes): Expr[String] = {
tpr.asType match {
case '[t] => getTypeString[t]
}
}
val tpr: TypeRepr = TypeRepr.of[T]
val typeParams: List[TypeRepr] = tpr match {
case a: AppliedType => a.args
case _ => Nil
}
val selfTag: Expr[ClassTag[T]] = getClassTag[T]
val argsStrings: Expr[List[String]] =
Expr.ofList(typeParams.map(getTypeStringRec))
'{ /* Compute something using selfTag and argsStrings */ }
}
private def getClassTag[T](using Type[T], Quotes): Expr[ClassTag[T]] = {
import quotes.reflect._
Expr.summon[ClassTag[T]] match {
case Some(ct) =>
ct
case None =>
report.error(
s"Unable to find a ClassTag for type ${Type.show[T]}",
Position.ofMacroExpansion
)
throw new Exception("Error when applying macro")
}
}