Index: doc/_common.html ================================================================== --- doc/_common.html +++ doc/_common.html @@ -24,11 +24,11 @@
関数はいわゆる「クロージャ」です。静的スコープで外側の環境にアクセスできます。 テーブルはいわゆるプロトタイプチェーンを持っていて、 自分にないフィールドの場合は親に問い合わせが行く感じになっていますが、 @@ -558,12 +567,11 @@ 動きとしてはこうです。
@macro
レイヤでコードを実行。@value
レイヤ、
- またはその関数を呼び出したときのレイヤで実行。
@macro
レイヤも所詮ただのレイヤですので、
上で説明した方法で @macro
レイヤに関数などを登録しておくことで、
構文木の生成をいじることが可能です。まさにマクロ。
@@ -657,12 +665,12 @@
reverseArgs は、関数呼び出しの構文木の、引数の順番を逆転する関数です。 @macro(e) によってマクロレイヤにセットされている構文木引数を取り出し、 それを @value レイヤによる普通の計算プログラムで操作しています。 -要は、@macro(...) はいわゆる「準クオート (quasiquote)」、 -@value(...) は「逆クオート (unquote)」に近い働きをします。 +@macro(...) はいわゆる「準クオート (quasiquote)」、 +@value(...) は「逆クオート (unquote)」にちょっと近いかもしれません。
@layer(...) だけでなく、関数のレイヤ指定引数なども同様に使うことができるので、 一部の引数は @macro、一部の引数は @value レイヤで受け取る関数を書くなど、 さらに色々面白いことが可能です。 @@ -698,60 +706,108 @@
-ここまで、@macro が本当にただの1レイヤであるかのように説明してきましたが、 -実はちょっと幾つかのトリックが潜んでいます。 +ここまで、@macro が本当にただの1レイヤと説明してきましたが、 +実はちょっとトリックが潜んでいます。
>> @macro twice(x) {x; x} in twice(@value(print("Hello"))) Hello Hello Hello
先ほどの例に @value を増やしたものですが、これでもやはり、Hello -が2回 print されるようになります。 +が2回 print されるようになります。これは本来はおかしな話で、print("Hello") +は @value レイヤで実行されて値に落ちるはずなので、1回しか print されないはず。 +
++実は、Polemy の中では、@macro レイヤと (rawmacro) +レイヤという二つの異なるマクロ用レイヤが動いています。 +
++ユーザーから直接 (rawmacro) は呼べませんが、 +「関数やトップレベル実行開始前のマクロ処理は (rawmacro) で実行開始」 +「@macro レイヤ以外で呼び出した関数の仮引数に @macro がついていたら、 +その実引数は (rawmacro) で実行」 +という2つのタイミングで (rawmacro) が動き出します。 +(rawmacro) が @macro レイヤから変数を見つけてマクロし始める時に、 +そこで @macro に動作が移ります。 +
++こうなっているのは、全部がレイヤ指定式に反応する @macro の動作だと、 +レイヤを使ったプログラムが全て @value 実行時ではなく、 +マクロ展開の時点で動き始めてしまって、おかしなことになるためです。 +色々考えた結果、とりあえずこの中途半端な混合が具合がよいのではないかということになりました。 +
++「関数実行開始時に、まずマクロレイヤを実行」と書きましたが、この時、関数内関数まで辿りにいくので、 +何重にもネストした関数を使っていると、内側の関数は、何重にもマクロ展開が走ってしまいます。 +これはなにかおかしい気がしますね。Scheme などはどうなっているのか調べないと…。 +
++これはエラーになります。
-@macro レイヤと (rawmacro) レイヤという二つが協調して動作しています。 - (rawmacro) レイヤの話 - - [[limitations]] - - This @macro layer is a very primitive one, and not a perfect macro language. - Two major limitations are seen in the following "it" example. - - >> @macro LetItBe(x, y) { let it = x in y }; - - The variable name is not hygenic, and so without any effort, the syntax tree "y" - can access the outer variable "it". - - >> def foo() { LetItBe( 1+2+3, it*it ) } - >> foo() - 36 - - Of course, this is not just a limitation; it can sometimes allow us to write - many interesting macros. - - The other problem is that the macro expansion is only done at function startup. - So - - >> LetItBe( 1+2+3, it*it ) - ...\value.d(173): [+:24:1] variable LetItBe is not set in layer @value - - you cannot directly use the macro in the same scope as the definition. - You need to wrap it up in a function (like the foo() in the above example). + >> let _ = (@macro twice(x) {x;x} in twice(print("Hello"))) + polemy.failure.RuntimeException@C:\Develop\Projects\Polemy\polemy\value.d(109): + [ :2:35] 'twice' is not set in @value layer
+どういうことかというと、@macro で定義したマクロはいつから使えるようになるかという話で、 +この @macro twice(x) {x;x} in ... の部分は @value レイヤの式なので、 +まずこの式全体のマクロ展開が終わったあとにしか実行されないのです。twice +がマクロと見なされはじめるのは、@macro 実行が終わった後。 +なので、 +例えば twice(print("Hello")) の部分を無名関数にラップしてやれば、 +マクロ展開を遅らせられて、 ちゃんと実行ができます。 +
++これだと余りにも不便なので、関数のトップレベルの変数宣言式の列についてだけは、 +@macro と @value の評価を交互にインターリーブするようにしました。 +「関数やREPLのトップレベルの最初に宣言したマクロだけは、その関数内で即座に使える」わけです。 +これも Scheme の let-syntax などなどの動きを調べて勉強しないと…。 +