2

マクロを使用して、次のようなオブジェクトをインスタンス化するコードを生成したいと考えています。

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])

シールされたトレイトのサブクラスを取得する方法を知ってc.WeakTypeTag[SubTest1]いるので、c.WeakTypeTag[SubTest2]マクロ コードでアクセスできます。TypeTagしかし、メソッドによって期待される sに変換する方法がわかりませんExample.applyin()ユニバース間で 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")
    }
  }
}
4

2 に答える 2

2

ここでランタイム タイプ タグを指定することについて心配する必要はありません。コンパイラがそれを見つけてくれます (別の回答でghikのメモを参照)。トリックはtoType、代わりに子クラスの型シンボルで使用することですtypeSignature:

object ExampleMacro {
  def collect[T] = macro collect_impl[T]

  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.collect {
        case child: TypeSymbol => q"Example[${child.toType}]"
      }

      c.Expr[Seq[Example[_]]](
        Apply(Select(reify(Seq).tree, newTermName("apply")), args)
      ) // or just c.Expr[Seq[Example[_]]](q"Seq(..$args)")
    } else c.abort(
      c.enclosingPosition,
      "Can only construct sequence from sealed trait"
    )
  }
}

ここではわかりやすくするために準引用符を使用しました。これは 2.10 プロジェクトで簡単に利用できるようになったためです。

于 2013-08-28T14:55:03.120 に答える