182

いくつかの型メンバーまたはメソッドを持つ無名クラスを定義し、それらのメソッドなどを持つ構造型として静的に型指定されたそのクラスのインスタンスを作成するマクロを書きたいとします。これは 2.10 のマクロ システムで可能です。 0 であり、型メンバー部分は非常に簡単です。

object MacroExample extends ReflectionUtils {
  import scala.language.experimental.macros
  import scala.reflect.macros.Context

  def foo(name: String): Any = macro foo_impl
  def foo_impl(c: Context)(name: c.Expr[String]) = {
    import c.universe._

    val Literal(Constant(lit: String)) = name.tree
    val anon = newTypeName(c.fresh)

    c.Expr(Block(
      ClassDef(
        Modifiers(Flag.FINAL), anon, Nil, Template(
          Nil, emptyValDef, List(
            constructor(c.universe),
            TypeDef(Modifiers(), newTypeName(lit), Nil, TypeTree(typeOf[Int]))
          )
        )
      ),
      Apply(Select(New(Ident(anon)), nme.CONSTRUCTOR), Nil)
    ))
  }
}

(私のメソッドを提供ReflectionUtilsする便利な特性はどこにありますかconstructor。)

このマクロを使用すると、匿名クラスの型メンバーの名前を文字列リテラルとして指定できます。

scala> MacroExample.foo("T")
res0: AnyRef{type T = Int} = $1$$1@7da533f6

適切に入力されていることに注意してください。すべてが期待どおりに機能していることを確認できます。

scala> implicitly[res0.T =:= Int]
res1: =:=[res0.T,Int] = <function1>

次に、メソッドを使用して同じことをしようとします。

def bar(name: String): Any = macro bar_impl
def bar_impl(c: Context)(name: c.Expr[String]) = {
  import c.universe._

  val Literal(Constant(lit: String)) = name.tree
  val anon = newTypeName(c.fresh)

  c.Expr(Block(
    ClassDef(
      Modifiers(Flag.FINAL), anon, Nil, Template(
        Nil, emptyValDef, List(
          constructor(c.universe),
          DefDef(
            Modifiers(), newTermName(lit), Nil, Nil, TypeTree(),
            c.literal(42).tree
          )
        )
      )
    ),
    Apply(Select(New(Ident(anon)), nme.CONSTRUCTOR), Nil)
  ))
}

しかし、試してみると、構造型が得られません。

scala> MacroExample.bar("test")
res1: AnyRef = $1$$1@da12492

しかし、そこに匿名クラスを追加すると、次のようになります。

def baz(name: String): Any = macro baz_impl
def baz_impl(c: Context)(name: c.Expr[String]) = {
  import c.universe._

  val Literal(Constant(lit: String)) = name.tree
  val anon = newTypeName(c.fresh)
  val wrapper = newTypeName(c.fresh)

  c.Expr(Block(
    ClassDef(
      Modifiers(), anon, Nil, Template(
        Nil, emptyValDef, List(
          constructor(c.universe),
          DefDef(
            Modifiers(), newTermName(lit), Nil, Nil, TypeTree(),
            c.literal(42).tree
          )
        )
      )
    ),
    ClassDef(
      Modifiers(Flag.FINAL), wrapper, Nil,
      Template(Ident(anon) :: Nil, emptyValDef, constructor(c.universe) :: Nil)
    ),
    Apply(Select(New(Ident(wrapper)), nme.CONSTRUCTOR), Nil)
  ))
}

できます:

scala> MacroExample.baz("test")
res0: AnyRef{def test: Int} = $2$$1@6663f834

scala> res0.test
res1: Int = 42

これは非常に便利です。たとえば、このようなことができますが、なぜ機能するのかわかりません。型メンバーのバージョンは機能しますが、機能しませんbarこれは定義された動作ではない可能性があることはわかっていますが、意味はありますか? マクロから構造型 (メソッドを含む) を取得するよりクリーンな方法はありますか?

4

1 に答える 1