我有以下宏,它正在为源类的给定访问器构造一些 Field 类:
case class Field[S, F](name: String, lens: Lens[S, F])
class Fields[S] {
def field[D](property: S => D): Field[S, D] = macro FieldsMacro.impl[S, D]
}
class FieldsMacro(val c: whitebox.Context) {
import c.universe._
def impl[S: c.WeakTypeTag, D: c.WeakTypeTag](property: c.Expr[S => D]): c.Expr[Field[S, D]] = {
val sourceType = weakTypeOf[S]
val destinationType = weakTypeOf[D]
val field = sourceType.decls.collect {
case m: MethodSymbol if m.isCaseAccessor =>
val methodName = m.name.decodedName.toString
val q"($x) => $x2.$name" = property.tree
if (name.toString().equals(methodName)) {
Some(q"Field($methodName, Lens[${sourceType.typeSymbol}, ${destinationType.typeSymbol}]($property)(_ => x => x))")
} else None
}.filter(_.isDefined).map(_.get).head
val result =
q"""
//some imports
$field
"""
println(showCode(result))
c.Expr[Field[S, D]](result)
}
}
用法示例:
case class TestClass(i: Int, s: String, seq: Seq[Int])
object TestObject extends Fields[TestClass] {
val i = field[Int](_.i)
val seq = field[Seq[Int]](_.seq)
}
问题是weakTypeOf[D]
返回Seq
而不是期望Seq[Int]
。所以我需要在宏中传递复杂的完整类型,比如Map[String,Int]
or List[Option[SomeClass]]
,所以我可以用准引号替换它。