我想使用宏來生成代碼實例看起來像這樣的對象:斯卡拉宏:轉換語境#TypeTag到JavaUniverse#TypeTag
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
實例。
不過,我想生成代碼實例化此類不同T
s,其中混凝土T
小號依賴於用戶輸入的幾個實例,像
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]
。但我不知道如何將它們變成TypeTag
s預期的方法Example.apply
。我想用in()
方法,似乎讓宇宙之間轉移TypeTag
S的,但它需要的目的地鏡子,我不知道如何在編譯時從宏內部得到運行反射。
這是我迄今(我增加了一些註釋和額外的報表也更清晰)代碼:你正在尋找這個
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")
}
}
}
哇,太好了。我想我應該先嚐試一下,但我認爲編譯器不是那麼聰明。顯然,我錯了)非常感謝。 –