scala 3 宏:从宏返回 Map[String, Class[Any]]
scala 3 macros: returning Map[String, Class[Any]] from a Macro
我正在尝试编写针对 scala 3.0.0-M3 的宏。我希望宏 return 类型的 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 id: Option[Int], 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
我做错了什么?我不应该能够 return 从宏中获取地图,或者不能这样?
请注意,我的宏中的 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[?]
。
然后你可以return打字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)
我正在尝试编写针对 scala 3.0.0-M3 的宏。我希望宏 return 类型的 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 id: Option[Int], 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
我做错了什么?我不应该能够 return 从宏中获取地图,或者不能这样?
请注意,我的宏中的 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[?]
。
然后你可以return打字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)