scalascala-macrosscala-3

Scala 3 Manifest replacement


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:


Solution

  • 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])
      }