2012-06-14 8 views
8

私はDSLを開発していますが、マクロを展開しているときに「フリーターム」エラーが発生しています。私はそれが避けることができるかどうか知りたいです。私はこの問題を次のような状況に単純化しました。参加このフリー・ターム・バリアブル・エラー(マクロ展開時に生成される)を回避できますか?

val list = join { 
    0 
    1 
    2 
    3 
} 
println(list) 

その実装であるマクロです:

def join(c: Ctx)(a: c.Expr[Int]): c.Expr[List[Int]] = { 
    import c.mirror._ 
    a.tree match { 
    case Block(list, ret) => 
     // c.reify(List(new c.Expr(list(0)).eval, 
     //    new c.Expr(list(1)).eval, 
     //    new c.Expr(list(2)).eval) :+ new c.Expr(ret).eval) 
     c.reify((for (expr <- list) yield new c.Expr(expr).eval) :+ new c.Expr(ret).eval) 
    } 
} 

マクロの目的は、引数ブロックと引き換えにすべての要素を結合するためにある

は、我々はこの式があるとしそれらを1つのリストにまとめます。ブロックの内容が可変である可能性があるので、コメント付きのreifyを使うことはできません。コメントのないコメントは、メッセージをスローします。

"Macros.scala:48:18の結合で定義された無料の変数リストがマクロ展開に含まれています。 reifeeにスプライシングこの変数を使用すると、自由用語変数を追跡悩みを持っている場合は?、-Xlogフリー-用語を使用することを検討してください」

は用-理解(またはイテレータまたは何でも)を導入する方法はありますこれを取得せずエラー?ところで、私は2.10-M3を使用しています。

答えて

15

問題は、コードがコンパイル時と実行時の概念を混在させることです。

あなたが使用している "list"変数はコンパイル時の値です(つまり、コンパイル時に反復されることになっています)。そして、実行時まで保持するように要求しています値)。このクロスステージの難点はいわゆるフリータームの作成につながる。

簡潔に言えば、フリーの用語は、前の段階の値を参照するスタブです。

val free$x1 = newFreeTerm("x", staticClass("scala.Int").asTypeConstructor, x); 
Ident(free$x1) 

賢い、ハァッを:たとえば、次のコード:以下のように

val x = 2 
reify(x) 

は、コンパイルされますか?結果は、xがIdentであり、型(コンパイル時の特性)を保持しますが、それにもかかわらずその値(実行時特性)も参照するという事実を保持します。これは、語彙的なスコープによって可能になる。

しかし、このツリーを(マクロの呼び出しサイトにインライン展開された)マクロ展開から戻そうとすると、事態が爆発します。マクロの呼び出しサイトは、そのレキシカルスコープではxをほとんど持たないため、xの値を参照することはできません。

さらに悪いのは何ですか?上記のスニペットがマクロの中に書かれている場合、xはコンパイル時にのみ、つまりコンパイラを実行するJVMにのみ存在します。しかし、コンパイラが終了すると、それはなくなりました。

しかし、xへの参照を含むマクロ展開の結果は、実行時に(おそらく、別のJVMで)実行されるはずです。これを理解するには、クロスステージ永続性、つまり、任意のコンパイル時の値を何らかの形でシリアル化し、実行時にデシリアライズする機能が必要です。私はScalaのようなコンパイル言語でこれを行う方法を知らない。


いくつかのケースでは、クロスステージ持続性が可能であることに注意してください。xは静的オブジェクトのフィールドだった場合たとえば、:

object Foo { val x = 2 } 
import Foo._ 
reify(x) 

そして、それは自由な用語として終わるませんが、簡単な方法で具体化されるだろう:

Select(Ident(staticModule("Foo")), newTermName("x")) 

ですScala Days 2012でのSPJの講演でも議論された興味深い概念です:http://skillsmatter.com/podcast/scala/haskell-cloud

いくつかの式に自由語が含まれていないことを確認するために、Haskellでは、コンパイラに新しい組み込みプリミティブ、型コンストラクタStaticを追加します。マクロでは、reify(それ自体は単なるマクロ)を使って自然に行うことができます。ここでの説明を参照:https://groups.google.com/forum/#!topic/scala-internals/-42PWNkQJNA


さて、元のコードの問題点を確認しました。どのように機能させるのですか?

reifiedはダイナミックなツリーを表現するのに苦労しているため、残念ながら手作業でASTを作成する必要があります。マクロロジーでのReifyの理想的な使用例は、マクロコンパイル時に知られている穴の種類を持つ静的テンプレートを持つことです。脇に歩いてください。手で木を建てることに頼らざるを得ません。

ボトムラインは、あなたが(:http://groups.google.com/group/scala-language/browse_thread/thread/bf079865ad42249cを正確に変更されているものを見るためにScalaの言語での移行ガイドを参照してください、最近リリース2.10.0-M4で動作します):次のように行かなければならないということです

import scala.reflect.makro.Context 

object Macros { 
    def join_impl(c: Context)(a: c.Expr[Int]): c.Expr[List[Int]] = { 
    import c.universe._ 
    import definitions._ 
    a.tree match { 
     case Block(list, ret) => 
     c.Expr((list :+ ret).foldRight(Ident(NilModule): Tree)((el, acc) => 
      Apply(Select(acc, newTermName("$colon$colon")), List(el)))) 
    } 
    } 

    def join(a: Int): List[Int] = macro join_impl 
} 
+0

スカラー2.11でもっと簡単なことがありますか?準引用符が役立つでしょうか? (私は深いところでマクロを勉強していませんが、私が試してみるといつもこれにぶつかります) – HRJ

+0

Quasiquotesは少し助けてくれるでしょう:https://gist.github.com/densh/6209261 –

関連する問題