【发布时间】:2013-08-28 22:57:47
【问题描述】:
我想使用宏来生成实例化对象的代码,如下所示:
import scala.reflect.runtime.universe._
case class Example[T: TypeTag] {
val tpe = implicitly[TypeTag[T]].tpe
}
显然,这会转化为如下内容:
import scala.reflect.runtime.universe._
case class Example[T](implicit ev: TypeTag[T]) {
val tpe = ev.tpe
}
如果这个类在常规代码中被实例化,Scala 编译器会自动提供TypeTag 实例。
但是,我想生成用不同的Ts 实例化此类的多个实例的代码,其中具体的Ts 取决于用户输入,例如
sealed trait Test
case class SubTest1 extends Test
case class SubTest2 extends Test
val examples = generate[Test]
// I want this ^^^^^^^^^^^^^^ to expand into this:
val examples = Seq(Example[SubTest1], Example[SubTest2])
我know 如何获取密封特征的子类,所以我可以在宏代码中访问c.WeakTypeTag[SubTest1] 和c.WeakTypeTag[SubTest2]。但我不知道如何将它们转换为Example.apply 方法所期望的TypeTags。我想过使用in() 方法,它似乎允许在宇宙之间传输TypeTags,但它需要目标镜像,我不知道如何在编译时从宏内部获取运行时镜像。
这是我到目前为止的代码(我添加了几个注释和额外的语句以使其更清晰):
object ExampleMacro {
def collect[T] = macro collect_impl
def collect_impl[T: c.WeakTypeTag](c: Context): c.Expr[Seq[Example[_]]] = {
import c.universe._
val symbol = weakTypeOf[T].typeSymbol
if (symbol.isClass && symbol.asClass.isTrait && symbol.asClass.isSealed) {
val children = symbol.asClass.knownDirectSubclasses.toList
if (!children.forall(c => c.isClass && c.asClass.isCaseClass)) {
c.abort(c.enclosingPosition, "All children of sealed trait must be case classes")
}
val args: List[c.Tree] = children.map { ch: Symbol =>
val childTpe = c.WeakTypeTag(ch.typeSignature) // or c.TypeTag(ch.typeSignature)
val runtimeChildTpe: c.Expr[scala.reflect.runtime.universe.TypeTag[_]] = ??? // What should go here?
Apply(Select(reify(Example).tree, newTermName("apply")), runtimeChildTpe.tree)
}
Apply(Select(reify(Seq).tree, newTermName("apply")), args)
} else {
c.abort(c.enclosingPosition, "Can only construct sequence from sealed trait")
}
}
}
【问题讨论】:
标签: scala reflection macros scala-macros