8

Scala 2.10MethodSymbolでaをメソッド定義ツリーの左側(つまり、 )に変える便利な方法はありますか?DefDef

たとえば、トレイトのインスタンスを取得し、そのトレイトのすべてのメソッドをいくつかのデバッグ機能でラップするマクロを作成するとします。私は次のように書くことができます:

import scala.language.experimental.macros
import scala.reflect.macros.Context

object WrapperExample {
  def wrap[A](a: A): A = macro wrap_impl[A]

  def wrap_impl[A: c.WeakTypeTag](c: Context)(a: c.Expr[A]) = {
    import c.universe._

    val wrapped = weakTypeOf[A]
    val f = Select(reify(Predef).tree, "println")

    val methods = wrapped.declarations.collect {
      case m: MethodSymbol if !m.isConstructor => DefDef(
        Modifiers(Flag.OVERRIDE),
        m.name,
        Nil, Nil,
        TypeTree(),
        Block(
          Apply(f, c.literal("Calling: " + m.name.decoded).tree :: Nil),
          Select(a.tree, m.name)
        )
      )
    }.toList

  //...
}

トレイトを実装する新しい匿名クラスにこれらのメソッドを固定し、そのクラスをインスタンス化するという退屈なビジネスを排除しました。興味がある場合は、ここで完全な実例を見つけることができます。

今、私はこれを書くことができます、例えば:

scala> trait X { def foo = 1; def bar = 'a }
defined trait X

scala> val x = new X {}
x: X = $anon$1@15dd533

scala> val w: X = WrapperExample.wrap[X](x)
w: X = $1$$1@27c3a4a3

scala> w.foo
Calling: foo
res0: Int = 1

scala> w.bar
Calling: bar
res1: Symbol = 'a

したがって、これは機能しますが、非常に単純な場合にのみ機能します。トレイトに、パラメーターリスト、アクセス修飾子、アノテーションなどを備えたメソッドがある場合は機能しません。

私が本当に欲しいのは、新しいボディのメソッドシンボルとツリーを受け取り、を返す関数ですDefDef。手作業で書き始めましたが、次のような面倒な作業がたくさん含まれています。

List(if (method.isImplicit) Some(Flag.IMPLICIT) else None, ...)

これは、煩わしく、冗長で、エラーが発生しやすいものです。新しいReflectionAPIでこれを行うためのより良い方法がありませんか?

4

2 に答える 2

4

私の知る限り、シンボルから定義ツリーに移行する標準的な方法はありません。

あなたの最善の策は、おそらく を反復処理しc.enclosingRun.units、進むにつれて各unit.bodyツリーに再帰することでしょう。DefDefシンボルに等しい が表示されている場合symbolは、目的地に到達しています。更新。duplicate再利用する前にツリーを定義することを忘れないでください!

この手法は、世界で最も便利なものとは言えませんが、うまくいくはずです。

于 2012-12-07T18:07:04.093 に答える
2

以下をお試しください。これは、複数のパラメーターとカリー化された関数と型パラメーターで機能します;)

val methods = wrapped.declarations.collect {
  case m: MethodSymbol if !m.isConstructor => DefDef(
    Modifiers(Flag.OVERRIDE),
    m.name,
    m.typeParams.map(TypeDef(_)),
    m.paramss.map(_.map(ValDef(_))),
    TypeTree(m.returnType),
    Block(
      Apply(f, c.literal("Calling: " + m.name.decoded).tree :: Nil),
      m.paramss.foldLeft(Select(a.tree.duplicate, m.name): Tree)((prev, params) =>
        Apply(prev, params.map(p => Ident(p.name)))
      )
    )
  )
}.toList
于 2012-12-08T02:28:35.957 に答える