マクロを使用して、次のようなオブジェクトをインスタンス化するコードを生成したいと考えています。
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
s はユーザー入力に依存します。
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])
シールされたトレイトのサブクラスを取得する方法を知ってc.WeakTypeTag[SubTest1]
いるので、c.WeakTypeTag[SubTest2]
マクロ コードでアクセスできます。TypeTag
しかし、メソッドによって期待される sに変換する方法がわかりませんExample.apply
。in()
ユニバース間で sを転送できると思われる方法を使用することを考えTypeTag
ましたが、宛先ミラーが必要であり、マクロ内からコンパイル時にランタイム ミラーを取得する方法がわかりません。
これまでのコードは次のとおりです(より明確にするために、いくつかの注釈と追加のステートメントを追加しました):
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")
}
}
}