scala 3 宏:从宏返回 Map[String, Class[Any]]

问题描述

我正在尝试针对 scala 3.0.0-M3 编写宏。我希望宏返回类型的 Option[_] 字段的内部类型。例如,给定:

class Professor(val lastName: String,val id: Option[Int],val bossId: Option[Long])

我想将 id 与 Int 相关联,并将 bossId 与 Long 相关联。

我有一些代码可以为原始类型执行此操作并且编译正常:

import scala.quoted._
import scala.quoted.staging._
import scala.quoted.{Quotes,Type}

object TypeInfo {


  inline def fieldsInfo[T <: AnyKind]: Map[String,Class[Any]] = ${ fieldsInfo[T] }

  def fieldsInfo[T <: AnyKind: Type](using qctx0: Quotes): Expr[Map[String,Class[Any]]] = {
    given qctx0.type = qctx0
    import qctx0.reflect.{given,_}

    val uns = TypeTree.of[T]
    val symbol = uns.symbol
    val innerClassOfOptionFields: Map[String,Class[Any]] = symbol.memberFields.flatMap { m =>
      // we only support val fields for Now
      if(m.isValDef){
        val tpe = ValDef(m,None).tpt.tpe
        // only if the field is an Option[_]
        if(tpe.typeSymbol == TypeRepr.of[Option[Any]].typeSymbol){
          val containedClass: Option[Class[Any]] =
            if(tpe =:= TypeRepr.of[Option[Int]]) Some(classOf[Int].asInstanceOf[Class[Any]])
            else if(tpe =:= TypeRepr.of[Option[Short]])  Some(classOf[Short].asInstanceOf[Class[Any]])
            else if(tpe =:= TypeRepr.of[Option[Long]])  Some(classOf[Long].asInstanceOf[Class[Any]])
            else if(tpe =:= TypeRepr.of[Option[Double]])  Some(classOf[Double].asInstanceOf[Class[Any]])
            else if(tpe =:= TypeRepr.of[Option[Float]])  Some(classOf[Float].asInstanceOf[Class[Any]])
            else if(tpe =:= TypeRepr.of[Option[Boolean]])  Some(classOf[Boolean].asInstanceOf[Class[Any]])
            else if(tpe =:= TypeRepr.of[Option[Byte]])  Some(classOf[Byte].asInstanceOf[Class[Any]])
            else if(tpe =:= TypeRepr.of[Option[Char]])  Some(classOf[Char].asInstanceOf[Class[Any]])
            else None

          containedClass.map(clazz => (m.name -> clazz))
        } else None
      } else None
    }.toMap

    println(innerClassOfOptionFields)

    Expr(innerClassOfOptionFields)
  }

但是如果我尝试使用它,就像这样:

class Professor(val lastName: String,val bossId: Option[Long])

object Main extends App {

  val fields = TypeInfo.fieldsInfo[Professor]

}

编译器首先打印 Map(id -> int,bossId -> long) 因为宏代码中的 println 看起来不错,但随后失败:

[error] 16 |  val fields = TypeInfo.fieldsInfo[Professor]
[error]    |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[error]    |               Found:    (classOf[Int] : Class[Int])
[error]    |               required: Class[Any]
[error]    | This location contains code that was inlined from Main.scala:34

我做错了什么?我不应该能够从宏返回 Map ,或者不应该这样?

请注意,我的宏中的 if/else 逻辑并不重要,问题可以简化为(其他条件相同):

    val result: Map[String,Class[Any]] = Map(
      "bossId" -> classOf[scala.Long].asInstanceOf[Class[Any]],"id" -> classOf[scala.Int].asInstanceOf[Class[Any]]
    )
    Expr(result)

解决方法

您可以根据 the one from the standard library 定义此缺失给定。

import scala.quoted._

given ToExpr[Class[?]] with {                               
  def apply(x: Class[?])(using Quotes) = {
    import quotes.reflect._
    Ref(defn.Predef_classOf).appliedToType(TypeRepr.typeConstructorOf(x)).asExpr.asInstanceOf[Expr[Class[?]]]
  }
}

在 Scala 3 的下一个版本中,这不再是必要的。标准库的给定实例 adapted 也适用于 Class[?]

然后您可以返回一个输入良好的 Map[String,Class[?]]

inline def fieldsInfo: Map[String,Class[?]] = ${ fieldsInfoMacro }

def fieldsInfoMacro(using Quotes): Expr[Map[String,Class[?]]] = {
  val result: Map[String,Class[?]] = Map(
    "bossId" -> classOf[scala.Long],"id" -> classOf[scala.Int]
  )
  Expr(result)
}

一切正常:

scala> fieldsInfo                                                               
val res1: Map[String,Class[?]] = Map(bossId -> long,id -> int)

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...