Diff
Not logged in

Differences From Artifact [a7325259b4f09952]:

To Artifact [d2cf6de49dcafd42]:


1 1 Ddoc 2 2 $(DDOC_AUTHORS k.inaba) 3 3 $(DDOC_LICENSE NYSL 0.9982 (http://www.kmonos.net/nysl/)) 4 4 5 5 <p> 6 -このファイルは、言語仕様などの簡単な説明です。 6 +左のサイドバーの "Package" タブをクリックすると実装のソースのドキュメントが読めます。 7 7 </p> 8 8 <p> 9 -あとついでに、左のサイドバーの "Package" タブをクリックすると実装のソースのドキュメントが読めます。 9 +このファイルは、言語仕様などの、やや辞書的な説明です。<br /> 10 +もっとざっくりとした、言語デザインの方向性の魂的なものについては、 11 +「メタプログラミングの会」の発表スライドをご覧下さい。 12 +</p> 13 +<p> 14 +あと、 やたらとマクロの章が長くなっていますが、 この部分は、 15 +レイヤ機能を入れたら自動的にすごく自然にマクロが入るなーと思って、 16 +おまけで実装してみた程度のものです。 17 +あんまり重要ではないので、適当にスルーして下さいませ。 18 +単に、適当に入れたら適当で微妙な部分が多く残ってしまったので注意書きが増えているだけで…。 10 19 </p> 11 20 12 21 $(DDOC_MEMBERS 13 22 14 23 $(SECTION Syntax, $(SECBODY 15 24 <p> 16 25 文法について。 ................................................................................ 171 180 </pre> 172 181 <p> 173 182 let-in を縦にチェインしたときだけ、同名変数を破壊的に上書きします 174 183 (再帰関数の定義が"うまく"いっているのはこの上書きのためです)。 175 184 なんでこんなことになっているかというと、 176 185 後で説明する「レイヤ」を使ったときに 177 186 <tt>let foo = ... in @lay foo = ... in ...</tt> 178 -で他レイヤに重ね書きするためであります。 187 +で他レイヤに重ね書きするため、のつもりです。詳しくは後で。 179 188 </p> 180 189 )) 181 190 ) 182 191 )) 183 192 184 193 185 194 ................................................................................ 203 212 以下のデータ型があります。 204 213 </p> 205 214 <ul> 206 215 <li>整数: <tt>0</tt>, <tt>123</tt>, <tt>456666666666666666666666666666666666666789</tt>, ...</li> 207 216 <li>文字列: <tt>"hello, world!"</tt>, ...</li> 208 217 <li>関数: <tt>fun(x){x+1}</tt></li> 209 218 <li>テーブル: <tt>{car: 1, cdr: {car: 2, cdr: {}}}</tt></li> 210 - <li>未定義値: (特殊なケースで作られます。「レイヤ」の説明参照のこと。)</li> 219 + <li>ボトム: (特殊なケースで作られます。「レイヤ」の説明参照のこと。)</li> 211 220 </ul> 212 221 <p> 213 222 関数はいわゆる「クロージャ」です。静的スコープで外側の環境にアクセスできます。 214 223 テーブルはいわゆるプロトタイプチェーンを持っていて、 215 224 自分にないフィールドの場合は親に問い合わせが行く感じになっていますが、 216 225 フィールドの書き換えがないので、これは特に意味ないかもしれない…。 217 226 </p> ................................................................................ 444 453 </pre> 445 454 <p> 446 455 動きとしてはこうです。 447 456 </p> 448 457 <ol> 449 458 <li>関数呼び出し時(とトップレベル環境の実行開始時)に、 450 459 まず、<code>@macro</code> レイヤでコードを実行。</li> 451 -<li>返ってきた構文木を、<code>@value</code> レイヤ、 452 - またはその関数を呼び出したときのレイヤで実行。</li> 460 +<li>返ってきた構文木を、その関数を呼び出したときのレイヤで実行。</li> 453 461 </ol> 454 462 <p> 455 463 <code>@macro</code> レイヤも所詮ただのレイヤですので、 456 464 上で説明した方法で <code>@macro</code> レイヤに関数などを登録しておくことで、 457 465 構文木の生成をいじることが可能です。まさにマクロ。 458 466 </p> 459 467 ................................................................................ 526 534 )}; 527 535 print( reverseArgs(1-2) ); $(D_COMMENT # 2-1 == 1) 528 536 </pre> 529 537 <p> 530 538 <tt>reverseArgs</tt> は、関数呼び出しの構文木の、引数の順番を逆転する関数です。 531 539 <tt>@macro(e)</tt> によってマクロレイヤにセットされている構文木引数を取り出し、 532 540 それを <tt>@value</tt> レイヤによる普通の計算プログラムで操作しています。 533 -要は、<tt>@macro(...)</tt> はいわゆる「準クオート (quasiquote)」、 534 -<tt>@value(...)</tt> は「逆クオート (unquote)」に近い働きをします。 541 +<tt>@macro(...)</tt> はいわゆる「準クオート (quasiquote)」、 542 +<tt>@value(...)</tt> は「逆クオート (unquote)」にちょっと近いかもしれません。 535 543 </p> 536 544 <p> 537 545 <tt>@layer(...)</tt> だけでなく、関数のレイヤ指定引数なども同様に使うことができるので、 538 546 一部の引数は <tt>@macro</tt>、一部の引数は <tt>@value</tt> レイヤで受け取る関数を書くなど、 539 547 さらに色々面白いことが可能です。 540 548 </p> 541 549 )) ................................................................................ 556 564 <p> 557 565 こんな感じのテーブルになります。 558 566 クラス名が <tt>is</tt> フィールドに、メンバ変数はそのままの名前で入ります。 559 567 配列メンバは cons リストになって入ってきます。 560 568 自分で構文木を作る時は、<tt>pos</tt> フィールドだけは省略しても構いません。 561 569 </p> 562 570 )) 563 -$(SECTION 微妙なところ, $(SECBODY 571 +$(SECTION 微妙なところ1, $(SECBODY 564 572 <p> 565 -ここまで、<tt>@macro</tt> が本当にただの1レイヤであるかのように説明してきましたが、 566 -実はちょっと幾つかのトリックが潜んでいます。 573 +ここまで、<tt>@macro</tt> が本当にただの1レイヤと説明してきましたが、 574 +実はちょっとトリックが潜んでいます。 567 575 </p> 568 576 <pre> 569 577 &gt;&gt; @macro twice(x) {x; x} in twice($(B @value)(print("Hello"))) 570 578 Hello 571 579 Hello 572 580 Hello 573 581 </pre> 574 582 <p> 575 583 先ほどの例に <tt>@value</tt> を増やしたものですが、これでもやはり、Hello 576 -が2回 print されるようになります。 584 +が2回 print されるようになります。これは本来はおかしな話で、<tt>print("Hello")</tt> 585 +は <tt>@value</tt> レイヤで実行されて値に落ちるはずなので、1回しか print されないはず。 586 +</p> 587 +<p> 588 +実は、Polemy の中では、<tt>@macro</tt> レイヤと <tt>(rawmacro)</tt> 589 +レイヤという二つの異なるマクロ用レイヤが動いています。 590 +</p> 591 +<ul> 592 + <li><tt>(rawmacro)</tt> も <tt>@macro</tt> も、コードを動かすとその構文木を返す意味論。</li> 593 + <li>ただし、<tt>(rawmacro)</tt> も <tt>@macro</tt> も、 594 + <tt>@macro</tt> レイヤに値のセットされた変数をみつけたときは、 595 + その変数という構文木を作るのではなく、変数の内容を展開。</li> 596 + <li>また <tt>@macro</tt> は、 597 + レイヤ指定式を見ると実行レイヤを切り替て、構文木生成モードをやめてしまう。</li> 598 + <li><tt>(rawmacro)</tt> は、 599 + レイヤ指定式を見ても実行レイヤを切り替えないで構文木にする。</li> 600 +</ul> 601 +<p> 602 +ユーザーから直接 <tt>(rawmacro)</tt> は呼べませんが、 603 +「関数やトップレベル実行開始前のマクロ処理は <tt>(rawmacro)</tt> で実行開始」 604 +「<tt>@macro</tt> レイヤ以外で呼び出した関数の仮引数に <tt>@macro</tt> がついていたら、 605 +その実引数は <tt>(rawmacro)</tt> で実行」 606 +という2つのタイミングで <tt>(rawmacro)</tt> が動き出します。 607 +<tt>(rawmacro)</tt> が <tt>@macro</tt> レイヤから変数を見つけてマクロし始める時に、 608 +そこで <tt>@macro</tt> に動作が移ります。 609 +</p> 610 +<p> 611 +こうなっているのは、全部がレイヤ指定式に反応する <tt>@macro</tt> の動作だと、 612 +レイヤを使ったプログラムが全て <tt>@value</tt> 実行時ではなく、 613 +マクロ展開の時点で動き始めてしまって、おかしなことになるためです。 614 +色々考えた結果、とりあえずこの中途半端な混合が具合がよいのではないかということになりました。 615 +</p> 616 +)) 617 +$(SECTION 微妙なところ2, $(SECBODY 618 +<p> 619 +「関数実行開始時に、まずマクロレイヤを実行」と書きましたが、この時、関数内関数まで辿りにいくので、 620 +何重にもネストした関数を使っていると、内側の関数は、何重にもマクロ展開が走ってしまいます。 621 +これはなにかおかしい気がしますね。Scheme などはどうなっているのか調べないと…。 622 +</p> 623 +)) 624 +$(SECTION 微妙なところ3, $(SECBODY 625 +<p> 626 +これはエラーになります。 577 627 </p> 578 628 <pre> 579 -<tt>@macro</tt> レイヤと <tt>(rawmacro)</tt> レイヤという二つが協調して動作しています。 580 - (rawmacro) レイヤの話 581 - 582 - [[limitations]] 583 - 584 - This @macro layer is a very primitive one, and not a perfect macro language. 585 - Two major limitations are seen in the following "it" example. 586 - 587 - >> @macro LetItBe(x, y) { let it = x in y }; 588 - 589 - The variable name is not hygenic, and so without any effort, the syntax tree "y" 590 - can access the outer variable "it". 591 - 592 - >> def foo() { LetItBe( 1+2+3, it*it ) } 593 - >> foo() 594 - 36 595 - 596 - Of course, this is not just a limitation; it can sometimes allow us to write 597 - many interesting macros. 598 - 599 - The other problem is that the macro expansion is only done at function startup. 600 - So 601 - 602 - >> LetItBe( 1+2+3, it*it ) 603 - ...\value.d(173): [<REPL>:24:1] variable LetItBe is not set in layer @value 604 - 605 - you cannot directly use the macro in the same scope as the definition. 606 - You need to wrap it up in a function (like the foo() in the above example). 629 + &gt;&gt; let _ = (@macro twice(x) {x;x} in twice(print("Hello"))) 630 + polemy.failure.RuntimeException@C:\Develop\Projects\Polemy\polemy\value.d(109): 631 + [<REPL>:2:35] 'twice' is not set in @value layer 607 632 </pre> 633 +<p> 634 +どういうことかというと、<tt>@macro</tt> で定義したマクロはいつから使えるようになるかという話で、 635 +この <tt>@macro twice(x) {x;x} in ...</tt> の部分は <tt>@value</tt> レイヤの式なので、 636 +まずこの式全体のマクロ展開が終わったあとにしか実行されないのです。<tt>twice</tt> 637 +がマクロと見なされはじめるのは、<tt>@macro</tt> 実行が終わった後。 638 +なので、 639 +例えば <tt>twice(print("Hello"))</tt> の部分を無名関数にラップしてやれば、 640 +マクロ展開を遅らせられて、 ちゃんと実行ができます。 641 +</p> 642 +<p> 643 +これだと余りにも不便なので、関数のトップレベルの変数宣言式の列についてだけは、 644 +<tt>@macro</tt> と <tt>@value</tt> の評価を交互にインターリーブするようにしました。 645 +「関数やREPLのトップレベルの最初に宣言したマクロだけは、その関数内で即座に使える」わけです。 646 +これも Scheme の let-syntax などなどの動きを調べて勉強しないと…。 647 +</p> 608 648 )) 609 649 ) 610 650 )) 611 651 612 652 613 653 $(SECTION Built-in Primitives, $(SECBODY 614 654 <p>