My task is to print out type information in Java-like notation (using <
, >
for type arguments notation). In scala 2 I have this small method using scala.reflect.Manifest
as a source for type symbol and it's parameters:
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)
}
Unfortunately in Scala 3 Manifests are not available. Is there a Scala 3 native way to rewrite this? I'm open to some inline macro stuff. What I have tried so far is
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?
I know that Scala types can't be all represented as Java types. I aim to cover only simple ones that the original method was able to cover. No wildcards or existentials, only fully resolved types like:
List[String]
res: List<String>
List[Option[String]]
res: List<Option<String>>
Map[String,Option[Int]]
res: Map<String,Option<Int>>
The final working solution I came up with was:
def typeOfImpl[T <: AnyKind: Type](using Quotes): Expr[String] = {
import quotes.reflect.*
def typeOfRepr(tpr: TypeRepr): Expr[String] = tpr.dealias.simplified match {
case AppliedType(tpr, args) =>
'{
${ typeOfRepr(tpr) } + ${ Expr.ofSeq(args.map(typeOfRepr)) }.mkString("<", ", ", ">"))
}
case TypeLambda(_, _, AppliedType(tpr, _)) => typeOfRepr(tpr)
case TypeBounds(l, u) if l =:= TypeRepr.of[Nothing] => '{ "? extends " + ${ typeOfRepr(u) } }
case tpr: TypeRef => Expr(tpr.show)
case other =>
report.errorAndAbort(s"unsupported type ${other.getClass}: ${other.show}", Position.ofMacroExpansion)
}
typeOfRepr(TypeRepr.of[T])
}