Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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:

  • List[String] res: List<String>
  • List[Option[String]] res: List<Option<String>>
  • Map[String,Option[Int]] res: Map<String,Option<Int>>
like image 996
SimY4 Avatar asked Dec 05 '25 14:12

SimY4


1 Answers

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")
  }

}
like image 60
Gaël J Avatar answered Dec 07 '25 07:12

Gaël J