Замена манифеста Scala 3

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

}