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>>I post this answer even though it's not a definitive solution and there's probably a better way but hopefully it can give you some ideas.
I think a good start is using TypeRepr:
val tpr: TypeRepr = TypeRepr.of[T]
val typeParams: List[TypeRepr] = tpr match {
case a: AppliedType => a.args
case _ => Nil
}
Then with a recursive method you should be able to work something out.
Copied from Inspired from 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")
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With