9

準引用符は驚くべきものです。これにより、Scala でマクロを作成する手間が大幅に軽減されます。私の経験では、ほとんどの場合、期待どおりに正確に機能します。そして何より、Scala 2.10でプラグインとして利用できるようになりました。

この質問は、このブログ投稿を書いているときに遭遇した小さな問題に関するものです。数分見つけられるときに調べたいもののリストに載っていますが、他の誰かが私を打ち負かすことができる場合に備えて、ここに投稿し、同じ質問に遭遇している他の人を助けると思いました.

名前と型のペアのリストのリストがあるとします。

val pss = List(
  List(newTermName("x") -> typeOf[Int], newTermName("y") -> typeOf[Char]),
  List(newTermName("z") -> typeOf[String])
)

これらを次のようなツリーに変換したいと思います。

def foo(x: Int, y: Char)(z: String) = ???

以下は問題なく動作します。

q"def bar(${pss.head.head._1}: ${pss.head.head._2}) = ???"

つまり、次のツリーを構築します。

def bar(x: Int) = ???

これは、次のようなものを書くことができるはずであることを示唆しています:

val quoted = pss.map(_.map { case (n, t) => q"$n: $t" })

q"def foo..${quoted.map(ps => q"($ps)")} = 1"

または、もう少し単純に、1 つのパラメーター リストに複数のパラメーターを指定します。

q"def baz(..${quoted.head}) = ???"

どちらも機能しません。次のようなエラーが表示されます。

<console>:28: error: type mismatch;
 found   : List[c.universe.Typed]
 required: List[c.universe.ValDef]
           q"def baz(..${quoted.head}) = ???"
                                ^

当然のことですが、 でパラメーターを定義するのではなく、型付き式を作成しているように、準クォーターにどのように見えるかがわかりますquoted。私が試してみようと思った明らかなことはどれもうまくいきませんでした( を追加= _する、準引用符を として明示的に入力するValDefなど)。

パラメータ定義を手動で作成できることはわかっています。

val valDefs = pss.map(
  _.map {
    case (n, t) => ValDef(Modifiers(Flag.PARAM), n, TypeTree(t), EmptyTree)
  }
)

そして今、bazバージョン (1 つのパラメーター リストを含む) が機能します。

q"def baz(..${valDefs.head}) = ???"

ただし、fooバージョン (複数のパラメーター リストを持つバージョン) ではありません。

そこで、ここで 2 つの質問があります。まず、quasiquotes を使用して、名前と型のペアをValDef、引用符で囲まれたパラメーター リストのコンテキスト外のパラメーターに変換するにはどうすればよいでしょうか? 次に、パラメーター定義のリストのリストを複数のパラメーター リストに変換するにはどうすればよいでしょうか。

いまいましいこと全体を手動の AST 構築に戻すのは簡単ですが (例については私の投稿を参照してください)、代わりに quasiquotes を使用できるようにしたいと考えています。

4

1 に答える 1

5

問題の簡単な解決策は次のとおりです。

val pss = List(
  List(newTermName("x") -> typeOf[Int], newTermName("y") -> typeOf[Char]),
  List(newTermName("z") -> typeOf[String])
)
val vparamss: List[List[ValDef]] = pss.map { _.map { case (name, tpe) => q"val $name: $tpe" } }
q"def foo(...$vparamss)"

ご覧のとおり、特別な ...$splice を使用すると、複数の引数リストを持つ関数を定義できます。関数の引数自体は、通常の val と同じ方法で表されます。

...$it が役立つ別の例:

val xy = List(List(q"x"), List(q"y"))
q"f(...$xy)" // same as q"f(x)(y)"
于 2013-09-05T19:08:54.957 に答える