@@ -2,12 +2,21 @@ $(DDOC_AUTHORS k.inaba) $(DDOC_LICENSE NYSL 0.9982 (http://www.kmonos.net/nysl/))

-このファイルは、言語仕様などの簡単な説明です。 +左のサイドバーの "Package" タブをクリックすると実装のソースのドキュメントが読めます。

-あとついでに、左のサイドバーの "Package" タブをクリックすると実装のソースのドキュメントが読めます。 +このファイルは、言語仕様などの、やや辞書的な説明です。
+もっとざっくりとした、言語デザインの方向性の魂的なものについては、 +「メタプログラミングの会」の発表スライドをご覧下さい。 +

+

+あと、 やたらとマクロの章が長くなっていますが、 この部分は、 +レイヤ機能を入れたら自動的にすごく自然にマクロが入るなーと思って、 +おまけで実装してみた程度のものです。 +あんまり重要ではないので、適当にスルーして下さいませ。 +単に、適当に入れたら適当で微妙な部分が多く残ってしまったので注意書きが増えているだけで…。

$(DDOC_MEMBERS @@ -174,9 +183,9 @@ (再帰関数の定義が"うまく"いっているのはこの上書きのためです)。 なんでこんなことになっているかというと、 後で説明する「レイヤ」を使ったときに let foo = ... in @lay foo = ... in ... -で他レイヤに重ね書きするためであります。 +で他レイヤに重ね書きするため、のつもりです。詳しくは後で。

)) ) )) @@ -206,9 +215,9 @@
  • 整数: 0, 123, 456666666666666666666666666666666666666789, ...
  • 文字列: "hello, world!", ...
  • 関数: fun(x){x+1}
  • テーブル: {car: 1, cdr: {car: 2, cdr: {}}}
  • -
  • 未定義値: (特殊なケースで作られます。「レイヤ」の説明参照のこと。)
  • +
  • ボトム: (特殊なケースで作られます。「レイヤ」の説明参照のこと。)
  • 関数はいわゆる「クロージャ」です。静的スコープで外側の環境にアクセスできます。 テーブルはいわゆるプロトタイプチェーンを持っていて、 @@ -447,10 +456,9 @@

    1. 関数呼び出し時(とトップレベル環境の実行開始時)に、 まず、@macro レイヤでコードを実行。
    2. -
    3. 返ってきた構文木を、@value レイヤ、 - またはその関数を呼び出したときのレイヤで実行。
    4. +
    5. 返ってきた構文木を、その関数を呼び出したときのレイヤで実行。

    @macro レイヤも所詮ただのレイヤですので、 上で説明した方法で @macro レイヤに関数などを登録しておくことで、 @@ -529,10 +537,10 @@

    reverseArgs は、関数呼び出しの構文木の、引数の順番を逆転する関数です。 @macro(e) によってマクロレイヤにセットされている構文木引数を取り出し、 それを @value レイヤによる普通の計算プログラムで操作しています。 -要は、@macro(...) はいわゆる「準クオート (quasiquote)」、 -@value(...) は「逆クオート (unquote)」に近い働きをします。 +@macro(...) はいわゆる「準クオート (quasiquote)」、 +@value(...) は「逆クオート (unquote)」にちょっと近いかもしれません。

    @layer(...) だけでなく、関数のレイヤ指定引数なども同様に使うことができるので、 一部の引数は @macro、一部の引数は @value レイヤで受け取る関数を書くなど、 @@ -559,12 +567,12 @@ 配列メンバは cons リストになって入ってきます。 自分で構文木を作る時は、pos フィールドだけは省略しても構いません。

    )) -$(SECTION 微妙なところ, $(SECBODY +$(SECTION 微妙なところ1, $(SECBODY

    -ここまで、@macro が本当にただの1レイヤであるかのように説明してきましたが、 -実はちょっと幾つかのトリックが潜んでいます。 +ここまで、@macro が本当にただの1レイヤと説明してきましたが、 +実はちょっとトリックが潜んでいます。

         >> @macro twice(x) {x; x} in twice($(B @value)(print("Hello")))
         Hello
    @@ -572,40 +580,72 @@
         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 実行時ではなく、 +マクロ展開の時点で動き始めてしまって、おかしなことになるためです。 +色々考えた結果、とりあえずこの中途半端な混合が具合がよいのではないかということになりました。 +

    +)) +$(SECTION 微妙なところ2, $(SECBODY +

    +「関数実行開始時に、まずマクロレイヤを実行」と書きましたが、この時、関数内関数まで辿りにいくので、 +何重にもネストした関数を使っていると、内側の関数は、何重にもマクロ展開が走ってしまいます。 +これはなにかおかしい気がしますね。Scheme などはどうなっているのか調べないと…。 +

    +)) +$(SECTION 微妙なところ3, $(SECBODY +

    +これはエラーになります。

    -@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 などなどの動きを調べて勉強しないと…。 +

    )) ) ))