Artifact Content
Not logged in

Artifact 33d07971634f698931f26b1904e702f0036e63a9


     1  Ddoc
     2  $(DDOC_AUTHORS k.inaba)
     3  $(DDOC_LICENSE NYSL 0.9982 (http://www.kmonos.net/nysl/))
     4  
     5  <p>
     6  左のサイドバーの "Package" タブをクリックすると実装のソースのドキュメントが読めます。
     7  </p>
     8  <p>
     9  このファイルは、言語仕様などの、やや辞書的な説明です。<br />
    10  もっとざっくりとした、言語デザインの方向性の魂的なものについては、
    11  「メタプログラミングの会」の発表スライドをご覧下さい。
    12  </p>
    13  <p>
    14  あと、 やたらとマクロの章が長くなっていますが、 この部分は、
    15  レイヤ機能を入れたら自動的にすごく自然にマクロが入るなーと思って、
    16  おまけで実装してみた程度のものです。
    17  あんまり重要ではないので、適当にスルーして下さいませ。
    18  単に、適当に入れたら適当で微妙な部分が多く残ってしまったので注意書きが増えているだけで…。
    19  </p>
    20  
    21  $(DDOC_MEMBERS
    22  
    23  $(SECTION Syntax, $(SECBODY
    24  <p>
    25  文法について。
    26  字句解析がわりと適当なので、
    27  変数宣言の変数名のところに、数字を変数名として使えて参照できない変数が作れたり、
    28  予約語は予約語として解釈され得ないところでは普通に変数名として使えちゃったりして、
    29  偶にとんでもない見かけのソースが構文解析通りますが、気にしないで適当に使って下さい。
    30  </p>
    31  
    32  $(DDOC_MEMBERS
    33  
    34  $(SECTION 文字コード, $(SECBODY
    35  <p>
    36  UTF-8 のみ対応です。
    37  </p>
    38  ))
    39  
    40  $(SECTION コメント, $(SECBODY
    41  <p>
    42  行コメントは <tt>#</tt> から改行までです。
    43  </p>
    44  <p>
    45  ブロックコメントはありません。
    46  </p>
    47  ))
    48  
    49  $(SECTION BNF, $(SECBODY
    50  <pre>
    51   ID    ::= 適当に識別子っぽい文字列
    52   LAYER ::= "@" ID
    53  
    54   E ::=
    55     $(D_COMMENT # 変数宣言)
    56       | DECL "=" E (";"|"in") E
    57       | DECL "(" PARAMS ")" "{" E "}" (";"|"in") E
    58       | DECL "=" E
    59       | DECL "(" PARAMS ")" "{" E "}"
    60  
    61           where DECL ::= ("var"|"let"|"def"|LAYER) ID | "@" LAYER
    62  
    63     $(D_COMMENT # リテラル)
    64       | INTEGER                        $(D_COMMENT # 非負整数)
    65       | STRING                         $(D_COMMENT # "" でくくった文字列。\" と \\ は使える)
    66       | "{" ENTRYS "}"                 $(D_COMMENT # テーブル)
    67       | "fun" "(" PARAMS ")" "{" E "}" $(D_COMMENT # 無名関数)
    68       |  "λ" "(" PARAMS ")" "{" E "}" $(D_COMMENT # 無名関数)
    69  
    70     $(D_COMMENT # 関数呼び出し)
    71       | E "(" ARGS")"
    72  
    73           where    ARGS ::= E "," ... "," E
    74                  PARAMS ::= (ID|LAYER)+ "," ... "," (ID|LAYER)+
    75                  ENTRYS ::= ID ":" E    "," ... "," ID ":" E
    76  
    77     $(D_COMMENT # 演算子など)
    78       | "(" E ")"                 $(D_COMMENT # ただの括弧)
    79       | "..."                     $(D_COMMENT # これを実行するとdie)
    80       | E BINOP E                 $(D_COMMENT # 二項演算子いろいろ)
    81       | E "."  ID                 $(D_COMMENT # テーブルのフィールドアクセス)
    82       | E ".?" ID                 $(D_COMMENT # テーブルにフィールドがあるか否か)
    83       | E "{" ENTRYS "}"          $(D_COMMENT # テーブル拡張)
    84       | "if" E ("then"|":"|"then" ":") E
    85       | "if" E ("then"|":"|"then" ":") E "else" ":"? E
    86  
    87     $(D_COMMENT # パターンマッチ)
    88       | "case" E ("when" PATTERN ":" E )* 
    89  
    90           where PATTERN ::= 式がだいたいなんでも書ける気がする
    91  
    92     $(D_COMMENT # レイヤ指定実行)
    93       | LAYER "(" E ")"
    94  </pre>
    95  ))
    96  
    97  $(SECTION 糖衣構文, $(SECBODY
    98  <p>
    99  演算子というものはありません。内部的には全て関数呼び出し構文に書き換えられています。<tt>if</tt> もです。
   100  <br/>
   101  パターンマッチも全部 <tt>if</tt> と <tt>==</tt> と <tt>&amp;&amp;</tt> と
   102  <tt>.</tt> と <tt>.?</tt> を使った関数呼び出し式に書き換えられていますが、
   103  規則の詳細を説明するのが面倒なので適当に想像して下さい。
   104  他の書き換えはこんな感じです。
   105  </p>
   106  <pre>
   107      if E then E         ⇒ if( E, fun(){E}, fun(){} )
   108      if E then E else E  ⇒ if( E, fun(){E}, fun(){E} )
   109      E BINOP E           ⇒ BINOP(E, E)
   110      { ENTRIES }         ⇒ {}{ ENTRIES }
   111      {}                  ⇒ {}()
   112      E {ID:E, ...}       ⇒ .=(E, ID, E) { ... }
   113  </pre>
   114  <p>
   115  変数宣言に色々ありますが、<tt>let</tt> と <tt>var</tt> と <tt>def</tt> は同じ扱いで、
   116  <tt>in</tt> と <tt>;</tt> は同じ扱いです。つまり
   117  </p>
   118  <pre>
   119     let x = E in E
   120     var x = E in E
   121     def x = E in E
   122     let x = E ; E
   123     var x = E ; E
   124     def x = E ; E
   125  </pre>
   126  <p>
   127  以上のどれも同じ意味なので、なんとなく関数型っぽく書きたい気分の日は <tt>let in</tt> を、
   128  手続き型っぽく書きたい気分の日は <tt>var ;</tt> を使うとよいと思います。
   129  <tt>if then else</tt> も微妙にコロンがあったりなかったりバリエーションがありますが好みで使います。
   130  </p>
   131  <p>
   132  関数を宣言するときは、<tt>fun</tt> や <tt>λ</tt> を省略できます。
   133  以下の書き換えが行われます。
   134  </p>
   135  <pre>
   136     def f( ARGS ) { E }; E   ⇒   def f = fun(ARGS){E}; E
   137  </pre>
   138  <p>
   139  他に、もっと手続き型っぽくための書き換え色々
   140  </p>
   141  <pre>
   142     fun () { E; E; E      }   ⇒   fun () { let _ = E in let _ = E in E }
   143     fun () { var x = 100  }   ⇒   fun () { var x = 100; x }
   144     fun () { var x = 100; }   ⇒   fun () { var x = 100; x }
   145     fun () { }                ⇒   fun () { "(empty function body)" }
   146  </pre>
   147  <p>
   148  中身が空の関数に何を返させるかは適当です。今はとりあえず適当に文字列返してます。
   149  </p>
   150  ))
   151  
   152  $(SECTION 変数のスコープ規則, $(SECBODY
   153  <p>
   154  基本的には、let によって常識的な感じに変数のスコープがネストします。
   155  </p>
   156  <pre>
   157     let x=21 in let x=x+x in x    $(D_COMMENT # 42)
   158  </pre>
   159  <p>
   160  一方で、"let rec" のような特別な構文はありませんが、
   161  </p>
   162  <pre>
   163     let f = fun(x) { if x==0 then 1 else x*f(x-1) } in f(10)  $(D_COMMENT # 3628800)
   164  </pre>
   165  <p>
   166  再帰的な関数定義なども、おそらく意図されたとおりに動きます。
   167  内部の詳細は、諸般の事情により、
   168  マジカルで破壊的なスコープ規則になっているのですが、
   169  同名の変数を激しく重ねて使ったりしなければ、
   170  だいたい自然な動きをすると思います、たぶん、はい。
   171  </p>
   172  <p>
   173  ひとつだけ不可思議な動きをするのは、以下のケースです。
   174  </p>
   175  <pre>
   176     let x = 1 in
   177     let f = fun() {x} in
   178     let x = 2 in
   179        f()    $(D_COMMENT # 2!!)
   180  </pre>
   181  <p>
   182  let-in を縦にチェインしたときだけ、同名変数を破壊的に上書きします
   183  (再帰関数の定義が"うまく"いっているのはこの上書きのためです)。
   184  なんでこんなことになっているかというと、
   185  後で説明する「レイヤ」を使ったときに
   186  <tt>let foo = ... in @lay foo = ... in ...</tt>
   187  で他レイヤに重ね書きするため、のつもりです。詳しくは後で。
   188  </p>
   189  ))
   190  )
   191  ))
   192  
   193  
   194  
   195  
   196  $(SECTION Basic Features, $(SECBODY
   197  <p>
   198  特に特徴的でもない部分を簡単にまとめ。
   199  </p>
   200  <ul>
   201    <li>静的型システムはありません。</li>
   202    <li>"ほぼ" 純粋関数型言語です。変数やテーブルのフィールドの破壊的な書き換えはできません。<br/>
   203        ただし、組み込み関数(<tt>print</tt>)と、変数のスコープ規則のマジカルな片隅に副作用があります。</li>
   204  </ul>
   205  <p>
   206  静的型システムがないのは意図的ですが、破壊的代入がないのは、単に実装がめんどかっただけなので、
   207  今後何か増えるかもしれません。増えないかもしれません。
   208  </p>
   209  $(DDOC_MEMBERS
   210  $(SECTION データ型, $(SECBODY
   211  <p>
   212  以下のデータ型があります。
   213  </p>
   214  <ul>
   215    <li>整数:     <tt>0</tt>, <tt>123</tt>, <tt>456666666666666666666666666666666666666789</tt>, ...</li>
   216    <li>文字列:   <tt>"hello, world!"</tt>, ...</li>
   217    <li>関数:     <tt>fun(x){x+1}</tt></li>
   218    <li>テーブル: <tt>{car: 1, cdr: {car: 2, cdr: {}}}</tt></li>
   219    <li>ボトム:   (特殊なケースで作られます。「レイヤ」の説明参照のこと。)</li>
   220  </ul>
   221  <p>
   222  関数はいわゆる「クロージャ」です。静的スコープで外側の環境にアクセスできます。
   223  テーブルはいわゆるプロトタイプチェーンを持っていて、
   224  自分にないフィールドの場合は親に問い合わせが行く感じになっていますが、
   225  フィールドの書き換えがないので、これは特に意味ないかもしれない…。
   226  </p>
   227  <p>
   228  また、リストを扱うために、いわゆる「cons リスト」を使います。
   229  空リストを <tt>{}</tt>、1個以上要素があるものを <tt>{car: 先頭要素, cdr: 二番目以降のリスト}</tt>
   230  という形で。この形でリストを扱わなければならないという決まりはありませんが、
   231  この形は特別扱いされて <tt>print</tt> で綺麗に出力されたりします。
   232  </p>
   233  ))
   234  $(SECTION パターンマッチ, $(SECBODY
   235  <p>
   236  適当に実装されたパターンマッチがあります。
   237  リストの 2n 番目と 2n+1 番目を足して長さを半分にする関数:
   238  </p>
   239  <pre>
   240      def adjSum(lst)
   241      {
   242        case lst
   243          when {car:x, cdr:{car: y, cdr:z}}: {car: x+y, cdr: adjSum(z)}
   244          when {car:x, cdr:{}}: lst
   245          when {}: {}
   246      }
   247  </pre>
   248  <p>
   249  動かすときには、処理系がそれっぽい if-then-else に展開しています。
   250  <tt>when</tt> を上から試していって、最初にマッチしたところを実行します。
   251  どれにもマッチしないとエラーでプログラム終了します。
   252  </p>
   253  <pre>
   254     PAT ::= "_"                                      $(D_COMMENT # ワイルドカード)
   255           | ID                                       $(D_COMMENT # 変数パターン)
   256           | "{" ID ":" PAT "," ... "," ID : PAT "}"  $(D_COMMENT # テーブルパターン)
   257           | E                                        $(D_COMMENT # 値パターン)
   258  </pre>
   259  <p>
   260  変数パターンは常にマッチして、値をその変数に束縛します。
   261  ワイルドカードも常にマッチしますが、変数束縛しません。
   262  値パターンは、任意の式が書けます。その式を評価した結果と <tt>==</tt> ならマッチします。
   263  外で束縛された変数を値パターンとして配置、は直接はできないので
   264  </p>
   265  <pre>
   266     var x = 123;
   267     case foo
   268       when {val: x+0}: ... $(D_COMMENT # これは {val:123} と同じ)
   269       when {val: x}:   ... $(D_COMMENT # これは任意の foo.?val なら常にマッチ)
   270  </pre>
   271  <p>
   272  適当にちょっと複雑な式にしてやるとよいかも(裏技)。
   273  </p>
   274  <p>
   275  テーブルパターンは、書かれたキーが全てあればマッチします。
   276  <tt>{a: _}</tt> は、<tt>.a</tt> を持ってさえいればマッチするので、
   277  <tt>{a: 123, b: 456}</tt> なんかにもマッチします。
   278  なので、リストに対するパターンを書くときには、car/cdr の場合を先に書かないと
   279  <tt>when {}</tt> を上に書くと全部マッチしちゃいます。注意。
   280  </p>
   281  ))
   282  )
   283  ))
   284  
   285  
   286  
   287  
   288  
   289  $(SECTION Layers, $(SECBODY
   290  <p>
   291  この言語の唯一の特徴的な部分は、「レイヤ」機能です。
   292  </p>
   293  <p>
   294  ひとつのコードに複数の「意味」を持たせるのが、レイヤ機能の目的です。
   295  </p>
   296  $(DDOC_MEMBERS
   297  $(SECTION 概要, $(SECBODY
   298  <p>
   299  普通に Polemy のコードを動かすと、そのコードは「<tt>@value</tt> レイヤ」で動作します。
   300  インタプリタで実験。
   301  </p>
   302  <pre>
   303      $ bin/polemy
   304      Welcome to Polemy 0.1.0
   305      &gt;&gt; 1 + 2
   306      3
   307  </pre>
   308  この、普通に、数字の 1 は数字の 1 として、2 は 2 として、足し算は足し算として実行するのが、
   309  「<tt>@value</tt> レイヤ」です。
   310  レイヤを明示的に指定するには、<tt>レイヤ名( ... )</tt> という構文を使います。
   311  $(RED $(B レイヤ指定式)) と読んでいます。
   312  つまり、さっきのコードは以下のようにも書けます。
   313  <pre>
   314      &gt;&gt; @value( 1 + 2 )
   315      3
   316  </pre>
   317  他のレイヤで動かしてみましょう。適当に。「<tt>@hoge</tt> レイヤ」で。
   318  <pre>
   319      &gt;&gt; @hoge( 3 )
   320      polemy.failure.RuntimeException@C:\Develop\Projects\Polemy\polemy\eval.d(138):
   321      [<REPL>:4:8] lift function for @hoge is not registered
   322  </pre>
   323  <p>
   324  エラーになりました。Polemy のインタプリタは、起動時には、<tt>@value</tt>
   325  レイヤでのコードの意味しか知りません。<tt>@hoge</tt> レイヤでは <tt>3</tt>
   326  というのがどんな意味なのか、わかりません!というエラーが出ています。
   327  </p>
   328  <p>
   329  これを教えてあげるためには、<tt>@hoge</tt> レイヤの $(RED $(B リフト関数)) というものを定義します。
   330  </p>
   331  <pre>
   332      &gt;&gt; @@hoge = fun(x){ x*2 }
   333      (function:1bdc5c0:1ba8580)
   334  </pre>
   335  <p>
   336  <tt>@hoge</tt> レイヤでは、<tt>1</tt> というコードの意味は <tt>2</tt>、
   337  <tt>2</tt> というコードの意味は <tt>4</tt>、…、という、全部「2倍した意味」を持っていることにします。
   338  「<tt>@ レイヤ名 = ...</tt>」 という構文を使います。
   339  ここには、「<tt>@value</tt> レイヤでの値 <tt>x</tt> は <tt>@hoge</tt> レイヤではどういう意味になるか?」
   340  を計算して返す関数を登録します。
   341  これで、Polemy にも、<tt>@hoge</tt> レイヤの意味がわかるようになりました。
   342  </p>
   343  <pre>
   344      &gt;&gt; @hoge( 3 )
   345      6
   346  </pre>
   347  <p>
   348  では、1+2 を <tt>@hoge</tt> レイヤで動かしてみましょう。
   349  </p>
   350  <pre>
   351      &gt;&gt; @hoge( 1 + 2 )
   352      polemy.failure.RuntimeException@C:\Develop\Projects\Polemy\polemy\eval.d(466):
   353      [<REPL>:3:7] only @value layer can call native function: +
   354      [<REPL>:3:7] +
   355  </pre>
   356  <p>
   357  まだエラーですね。これは要するに "+" の意味がわからない、と言っています。
   358  $(RED $(B レイヤ指定変数定義式)) で、"+" の意味を教えてあげます。
   359  </p>
   360  <pre>
   361      &gt;&gt; @hoge "+" = fun(x, y) {x}
   362      (function:182eca0:18435e0)
   363      &gt;&gt; @hoge( 3 + 4 )
   364      6
   365  </pre>
   366  <p>
   367  できました。
   368  </p>
   369  <p>
   370  他の組み込み関数の意味も決めてみましょう。この <tt>@hoge</tt> レイヤでは、
   371  引き算のつもりで書いたコードが、掛け算になってしまうのだ!
   372  </p>
   373  <pre>
   374      &gt;&gt; @hoge "-" = fun(x, y) {x * y}
   375      (function:1b4c6a0:1b4fbe0)
   376      &gt;&gt; @hoge( 5 - 6 )
   377      polemy.failure.RuntimeException@C:\Develop\Projects\Polemy\polemy\eval.d(469):
   378      [<REPL>:3:24] only @value layer can call native function: *
   379      [<REPL>:3:24] *
   380      [<REPL>:4:8] -
   381  </pre>
   382  <p>
   383  5、の意味は 10 で 6 の意味は 12 なので、10 - 12 と見せかけて掛け算して 120 が返るのだ!
   384  と思いきや、エラーになってしまいました。なぜでしょう。それは、この "-" の定義、
   385  <code>fun(x, y) {x * y}</code> 自体が、<tt>@hoge</tt> レイヤで実行されるからです。
   386  掛け算はまだ定義していません。
   387  </p>
   388  <p>
   389  ここは、「普通の」意味の掛け算を使いたいのです。
   390  この部分については、<tt>@value</tt> レイヤで計算して欲しい。
   391  そんなときは、レイヤ指定式を使います。
   392  </p>
   393  <pre>
   394      &gt;&gt; @hoge "-" = fun(x, y) {$(B @value(@hoge(x) * @hoge(y)))}
   395      (function:1b086c0:1b4fbe0)
   396      &gt;&gt; @hoge( 5 - 6 )
   397      120
   398  </pre>
   399  <p>
   400  できました。掛け算は、<tt>@value</tt> レイヤの意味で実行します。
   401  各変数は、<tt>@hoge</tt> レイヤで計算された意味を使います、という意味になります。
   402  </p>
   403  ))
   404  $(SECTION 関数の自動リフト, $(SECBODY
   405  <p>
   406  続きです。ちょっと関数を定義してみました。
   407  </p>
   408  <pre>
   409      &gt;&gt; def twoMinus(x,y,z) { x - y - z }
   410      (function:1b26420:1b4fbe0)
   411      &gt;&gt; twoMinus(1,2,3)
   412      -4
   413  </pre>
   414  <p>
   415  <tt>@value</tt> レイヤで実行すると、当然、1 から 2 と 3 を引いて、-4 です。
   416  </p>
   417  <pre>
   418      &gt;&gt; @hoge( twoMinus(1,2,3) )
   419      48
   420  </pre>
   421  <p>
   422  <tt>@hoge</tt> レイヤだと、2 と 4 と 6 を掛け算するので、結果は 48 です。
   423  </p>
   424  <p>
   425  1, 2, 3 のような値と、+ や - のような組み込み関数については、
   426  「<tt>@hoge</tt> レイヤでの意味」をレイヤを定義する人が決めてやる必要があります。
   427  でも、それさえ決めれば、あとはプログラム中で自分で定義した関数はすべて、
   428  Polemy 側で自動的にそのレイヤでの意味で実行できるようになります。
   429  </p>
   430  <p>
   431  レイヤ指定変数定義を使って、変数の意味をそのレイヤでだけ上書きして、
   432  違う意味を与えてやっても構いません。
   433  </p>
   434  <pre>
   435      &gt;&gt; def twoMinus(x,y,z) { x - y - z } $(D_COMMENT # @value レイヤでの定義)
   436      &gt;&gt; @hoge twoMinus(x,y,z) { 21 }      $(D_COMMENT # @hoge レイヤでの定義)
   437      &gt;&gt; twoMinus(1,2,3)
   438      -4
   439      &gt;&gt; @hoge( twoMinus(1,2,3) )
   440      42
   441  </pre>
   442  <p>
   443  こんな感じで。
   444  </p>
   445  ))
   446  $(SECTION レイヤ指定引数, $(SECBODY
   447  <p>
   448  ここまでのサンプルでは、コードを書いた人が、レイヤ指定式で明示的にレイヤを切り替えていました。
   449  $(RED $(B レイヤ指定引数)) を使うと、ライブラリ関数などを書くときに、
   450  「この関数の第2引数は <tt>@hoge</tt> レイヤで計算して欲しい」
   451  といった指定ができます。
   452  </p>
   453  <pre>
   454      &gt;&gt; def f(x, y $(B @hoge)) { x + @hoge(y) }
   455      &gt;&gt; f(1, 2)
   456      5
   457  </pre>
   458  <p>
   459  f の第2引数は、必ず <tt>@hoge</tt> レイヤで解釈されます。
   460  </p>
   461  <pre>
   462      &gt;&gt; def ff(x, y $(B @hoge @value)) { x + @hoge(y) + @value(y) }
   463      &gt;&gt; ff(1, 2)
   464      7
   465  </pre>
   466  <p>
   467  <tt>@hoge</tt> と <tt>@value</tt> の両方のレイヤで解釈して欲しい、という欲張りな人は、
   468  レイヤ指定を複数並べて下さい。
   469  </p>
   470  <p>
   471  なにもレイヤ指定がないと、$(RED $(B ニュートラルレイヤ指定)) と呼ばれ、
   472  その関数の呼び出し側が解釈されていたレイヤと同じところにセットされます。
   473  <tt>let</tt>, <tt>var</tt>, <tt>def</tt> による変数定義も同じで、
   474  <tt>@hoge x = ...</tt> とレイヤを明示するとそのレイヤでの変数の意味が定義されますが、
   475  <tt>let x = ...</tt> とレイヤ指定しないで書くと、現在解釈中のレイヤに定義、という動作をします。
   476  </p>
   477  ))
   478  $(SECTION ボトムと自動メモ化, $(SECBODY
   479  <p>
   480  パターンマッチ失敗時と、"..." という式を実行したときと、再帰が無限に止まらなくなったとき、
   481  には、Polemy のコードは実行時エラーで終了します……<tt>@value</tt> レイヤならば。
   482  </p>
   483  <p>
   484  ユーザー定義レイヤでは、このような時にも実行時エラーにならず、
   485  「$(RED $(B ボトム))」という特別な値がリフト関数に渡されます。
   486  組み込みの <tt>_isbot</tt> 関数で、ボトムかどうか判定できます。
   487  </p>
   488  <p>
   489  「再帰が無限に止まらなくなったとき」は、
   490  ある引数で呼び出された関数が、return するよりも前にまた同じ引数で呼び出されたら、
   491  ループしていると見なすことで判定しています。
   492  これを判定する実装の副作用として、ユーザー定義のレイヤでは、関数は全てメモ化されています。
   493  つまり、ある関数が2回同じ引数同じ環境で呼び出されたら、1回目の答えをキャッシュしておいて、
   494  2回目は計算をせずに即座にキャッシュをひいて答えを返します。
   495  </p>
   496  ))
   497  $(SECTION まとめ, $(SECBODY
   498  <p>
   499  まとめると、以下の機能があります。
   500  </p>
   501  <ul>
   502  	<li><tt>@@layer = fun(x) { ... } in ...</tt> で、
   503  		<tt>@value</tt> レイヤの値に別のレイヤでの意味を与えるリフト関数を定義</li>
   504  	<li><tt>@layer x = ... in ...</tt> で、そのレイヤでのその変数の意味を定義</li>
   505  	<li>どちらも let/var/def 式の特殊形なので、<tt>@@layer(x) { ... } in ...</tt> などの略記も可。</li>
   506  	<li>式の途中で @layer( ... ) と書くと、レイヤを明示的に切り替えられる</li>
   507  	<li>関数の仮引数に fun(x @layer){ ... } とレイヤを指定すると、
   508  		対応する実引数はそのレイヤで解釈される。</li>
   509  </ul>
   510  <p>
   511  </p>
   512  ))
   513  $(SECTION 例, $(SECBODY
   514  <p>
   515  具体的な「値」のかわりに、その「メタ情報」を取り出して、それが処理によってどう変化するか、
   516  といった情報を解析するのを主な用途として、この機能を作ってみました。
   517  プログラムでよく使われる代表的なメタ情報は、「型」です。
   518  サンプルとしては、sample/type.pmy をご覧下さい。以下、簡単な概略。
   519  </p>
   520  <pre>
   521      @@type = fun(x) {
   522         if( _isint(x) ) then "int"
   523         else if( _isstr(x) ) then "str"
   524         else if( _isbot(x) ) then "runtime error"
   525         else "type error"
   526      }
   527  </pre>
   528  <pre>
   529      &gt;&gt; @type( 1 )
   530      int
   531      &gt;&gt; @type( 2 )
   532      int
   533      &gt;&gt; @type( "foo" )
   534      str
   535  </pre>
   536  <p>
   537  こんな風に、値をメタ情報へ抽象化するのが、リフト関数です。
   538  </p>
   539  <p>
   540  型に抽象化したレイヤでの、組み込み関数の意味を考えましょう。
   541  "+" は、"int" と "int" を足したら "int" を返す関数です。
   542  それ以外なら"型エラー"を返します。そういう関数です。
   543  </p>
   544  <pre>
   545      var int_int_int = fun (x, y) {@value(
   546         var tx = @type(x);
   547         var ty = @type(y);
   548         if tx=="runtime error" then ty
   549         else if ty=="runtime error" then tx
   550         else if tx=="int" && ty=="int" then "int"
   551         else "type error"
   552      )};
   553  
   554      @type "+" = int_int_int;
   555      @type "-" = int_int_int;
   556      @type "<" = int_int_int;
   557  </pre>
   558  <pre>
   559      &gt;&gt; @type( 1 + 2 )
   560      int
   561      &gt;&gt; @type( 1 + "foo" )
   562      type error
   563  </pre>
   564  <p>
   565  「実行時エラーについては、それが起きなければ返すはずの型」を計算するという定義に、
   566  ここではしています。さらに(ちょっと手抜きで int 以外を考えていない)if の型定義を考えると、
   567  こんな雰囲気。
   568  </p>
   569  <pre>
   570     @type "if" (c, t, e) {@value(
   571        if( @type(c)=="int" || @type(c)=="runtime error" ) then
   572           @type( int_int_int(t(), e()) )
   573        else
   574           "type error"
   575     )}
   576  </pre>
   577  <p>
   578  関数が自動リフトされるので、フィボナッチ関数の型を調べることができます。
   579  </p>
   580  <pre>
   581     &gt;&gt; def fib(x) { if x<2 then 1 else fib(x-1)+fib(x-2) };
   582     &gt;&gt; @type( fib(100000000000000) )
   583     int
   584     &gt;&gt; def gib(x) { if x<2 then 1 else gib(x-1)+gib(x-"str") };
   585     &gt;&gt; @type( gib(100000000000000) )
   586     type error
   587  </pre>
   588  <p>
   589  この定義で <tt>fib(100000000000000)</tt> を <tt>@value</tt> レイヤで普通に計算して、
   590  結果の型を見る、というのでは時間がいくらあっても足りません。
   591  いったん <tt>@type</tt> のメタ情報の世界に移ってから計算できるのが、レイヤ機能の肝です。
   592  </p>
   593  <p>
   594  正確には、この定義で <tt>@type</tt> レイヤに移ると fib("int") を無限に呼び出し続けて止まらなくなるのですが、
   595  そこは、自動メモ化による再帰検出でボトム値を返す機能によって、うまく止まっています。
   596  </p>
   597  <p>
   598  それでも上手く型計算ができない(あるいはすごく遅くなる)ような複雑な関数があるかもしれません。
   599  仕方がないので、型情報をアノテーションとしてつけてあげることも可能です。
   600  </p>
   601  <pre>
   602     @type f = int_int_int;
   603       def f(x,y) { ...とても型を計算できないくらい複雑な定義... };
   604  </pre>
   605  <p>
   606  これが、レイヤ指定変数定義の典型的な使い道です。
   607  </p>
   608  ))
   609  )
   610  ))
   611  
   612  
   613  $(SECTION Macro Layers, $(SECBODY
   614  <p>
   615  Polemy 言語組み込みのレイヤは <code>@value</code> と <code>@macro</code> の二つです。
   616  (内部的にはもういくつかありますが、ユーザから直接は使えません。)
   617  <code>@value</code> は、「普通に」普通のセマンティクスでプログラムを実行するレイヤでした。
   618  <code>@macro</code> は、実は、<code>@value</code> よりも前に実行されるレイヤで、
   619  「プログラムを実行するとその構文木を返す」というセマンティクスで動きます。
   620  </p>
   621  <p>
   622  動きとしてはこうです。
   623  </p>
   624  <ol>
   625  <li>関数呼び出し時(とトップレベル環境の実行開始時)に、
   626  	まず、<code>@macro</code> レイヤでコードを実行。</li>
   627  <li>返ってきた構文木を、その関数を呼び出したときのレイヤで実行。</li>
   628  </ol>
   629  <p>
   630  <code>@macro</code> レイヤも所詮ただのレイヤですので、
   631  上で説明した方法で <code>@macro</code> レイヤに関数などを登録しておくことで、
   632  構文木の生成をいじることが可能です。まさにマクロ。
   633  </p>
   634  
   635  $(DDOC_MEMBERS
   636  $(SECTION 概要, $(SECBODY
   637  <p>
   638  samples/macro.pmy にいくつか使い方サンプルが置いてありますので、詳しくはそちらをどうぞ。
   639  </p>
   640  <pre>
   641      &gt;&gt; @macro( twice(print("Hello")) )
   642      {
   643        pos: {lineno:1, column:9, filename:<REPL>},
   644       args: [ { pos: {lineno:1, column:15, filename:<REPL>},
   645                args: [{pos:{lineno:1, column:21, filename:<REPL>},
   646                         is:Str,
   647                       data:Hello}],
   648                  is: App,
   649                 fun: {pos:{lineno:1, column:15, filename:<REPL>}, is:Var, name:print}}
   650             ],
   651         is: App,
   652        fun: {pos:{lineno:1, column:9, filename:<REPL>}, is:Var, name:twice}
   653      }
   654  </pre>
   655  <p>
   656  詳細は気にしなくて構いませんが、とにかく、<tt>@macro</tt> レイヤでは、
   657  基本的には、コードを実行するとそのコードの構文木がでてきます。
   658  この挙動は <tt>@macro</tt> レイヤの変数をセットすることで、カスタマイズできます。
   659  </p>
   660  <pre>
   661      &gt;&gt; @macro twice(x) { x; x } in twice(print("Hello"))
   662      Hello
   663      Hello
   664      Hello
   665  </pre>
   666  <p>
   667  (3回出力されてますが、3個目は <tt>print(x)</tt> の返値は <tt>x</tt> なので、
   668  それがREPLによって印字されているだけです。)
   669  <tt>@macro</tt> レイヤで <tt>in</tt> 以降を実行すると、<tt>print("Hello")</tt> という式を表す構文木が作られ、
   670  それが <tt>twice</tt> 関数に渡されます。<tt>twice</tt> の中身も <tt>@macro</tt> レイヤで実行されるので、
   671  構文木を作ろうとしますが、変数 <tt>x</tt> には <tt>@macro</tt> レイヤで値が入っているので、
   672  その値を読み取って構文木を作成します。
   673  結果として、2回 <tt>print("Hello")</tt> する構文木が作られて、
   674  その後で、それが <tt>@value</tt> レイヤで実行されています。
   675  </p>
   676  <p>
   677  本当にベタに構文木を作るだけなので、変数名の衝突などなどは気にしません。「衛生的でない」マクロです。
   678  </p>
   679  <pre>
   680      @macro LetItBe(x, y) { var $(B it) = x; y };  $(D_COMMENT # y の中で変数 it が使える)
   681      print( LetItBe("myself",  "when I find " ~ $(B it) ~ " in times of trouble") );
   682  </pre>
   683  <p>
   684  変数名に気をつけるには、組み込み関数 <tt>gensym()</tt> を使って頑張って下さい。
   685  </p>
   686  ))
   687  $(SECTION レイヤ切り替え, $(SECBODY
   688  <p>
   689  他のレイヤ同様、<tt>@macro</tt> レイヤを実行中に <tt>@layer( ... )</tt> 構文を使うことで、
   690  別のレイヤでコードを動かすこともできます。よく使う例は、<tt>@value</tt>
   691  レイヤに移ることで構文木を普通に計算して色々プログラム的にいじる用途です。
   692  </p>
   693  <pre>
   694      @macro reverseArgs(e) {$(B @value)(
   695          def rev(xs, acc) {
   696            case xs when {car:x, cdr:xs}: rev(xs, {car:x, cdr:acc}) when {}: acc
   697          };
   698          case @macro(e)
   699            when {is:"App", fun:f, args:as}: {is:"App", fun:f, args:rev(as,{})}
   700            when e: e
   701      )};
   702      print( reverseArgs(1-2) ); $(D_COMMENT # 2-1 == 1)
   703  </pre>
   704  <p>
   705  <tt>reverseArgs</tt> は、関数呼び出しの構文木の、引数の順番を逆転する関数です。
   706  <tt>@macro(e)</tt> によってマクロレイヤにセットされている構文木引数を取り出し、
   707  それを <tt>@value</tt> レイヤによる普通の計算プログラムで操作しています。
   708  <tt>@macro(...)</tt> はいわゆる「準クオート (quasiquote)」、
   709  <tt>@value(...)</tt> は「逆クオート (unquote)」にちょっと近いかもしれません。
   710  </p>
   711  <p>
   712  <tt>@layer(...)</tt> だけでなく、関数のレイヤ指定引数なども同様に使うことができるので、
   713  一部の引数は <tt>@macro</tt>、一部の引数は <tt>@value</tt> レイヤで受け取る関数を書くなど、
   714  さらに色々面白いことが可能です。
   715  </p>
   716  ))
   717  $(SECTION 構文木の構造, $(SECBODY
   718  <p>
   719  構文木がどのようなテーブルで渡されてくるかについては、ソースドキュメントの
   720  <a href="http://www.kmonos.net/repos/polemy/doc/tip/doc/ast.html">polemy.ast</a>
   721  のページをご覧下さい。例えば変数名を表す <code>Var</code> クラスには、
   722  継承の分も合わせて
   723  <tt><a href="http://www.kmonos.net/repos/polemy/doc/tip/doc/failure.html">LexPosition</a> pos;</tt>
   724  と <tt>string name;</tt> の2つのメンバがあるので
   725  </p>
   726  <pre>
   727      { is:   "Var",
   728        pos:  {filename:"foo.pmy", lineno:123, column:45},
   729        name: "x" }
   730  </pre>
   731  <p>
   732  こんな感じのテーブルになります。
   733  クラス名が <tt>is</tt> フィールドに、メンバ変数はそのままの名前で入ります。
   734  配列メンバは cons リストになって入ってきます。
   735  自分で構文木を作る時は、<tt>pos</tt> フィールドだけは省略しても構いません。
   736  </p>
   737  ))
   738  $(SECTION 微妙なところ1, $(SECBODY
   739  <p>
   740  ここまで、<tt>@macro</tt> が本当にただの1レイヤと説明してきましたが、
   741  実はちょっとトリックが潜んでいます。
   742  </p>
   743  <pre>
   744      &gt;&gt; @macro twice(x) {x; x} in twice($(B @value)(print("Hello")))
   745      Hello
   746      Hello
   747      Hello
   748  </pre>
   749  <p>
   750  先ほどの例に <tt>@value</tt> を増やしたものですが、これでもやはり、Hello
   751  が2回 print されるようになります。これは本来はおかしな話で、<tt>print("Hello")</tt>
   752  は <tt>@value</tt> レイヤで実行されて値に落ちるはずなので、1回しか print されないはず。
   753  </p>
   754  <p>
   755  実は、Polemy の中では、<tt>@macro</tt> レイヤと <tt>(rawmacro)</tt>
   756  レイヤという二つの異なるマクロ用レイヤが動いています。
   757  </p>
   758  <ul>
   759  	<li><tt>(rawmacro)</tt> も <tt>@macro</tt> も、コードを動かすとその構文木を返す意味論。</li>
   760  	<li>ただし、<tt>(rawmacro)</tt> も <tt>@macro</tt> も、
   761  		<tt>@macro</tt> レイヤに値のセットされた変数をみつけたときは、
   762  		その変数という構文木を作るのではなく、変数の内容を展開。</li>
   763  	<li>また <tt>@macro</tt> は、
   764  		レイヤ指定式を見ると実行レイヤを切り替て、構文木生成モードをやめてしまう。</li>
   765  	<li><tt>(rawmacro)</tt> は、
   766  		レイヤ指定式を見ても実行レイヤを切り替えないで構文木にする。</li>
   767  </ul>
   768  <p>
   769  ユーザーから直接 <tt>(rawmacro)</tt> は呼べませんが、
   770  「関数やトップレベル実行開始前のマクロ処理は <tt>(rawmacro)</tt> で実行開始」
   771  「<tt>@macro</tt> レイヤ以外で呼び出した関数の仮引数に <tt>@macro</tt> がついていたら、
   772  その実引数は <tt>(rawmacro)</tt> で実行」
   773  という2つのタイミングで <tt>(rawmacro)</tt> が動き出します。
   774  <tt>(rawmacro)</tt> が <tt>@macro</tt> レイヤから変数を見つけてマクロし始める時に、
   775  そこで <tt>@macro</tt> に動作が移ります。
   776  </p>
   777  <p>
   778  こうなっているのは、全部がレイヤ指定式に反応する <tt>@macro</tt> の動作だと、
   779  レイヤを使ったプログラムが全て <tt>@value</tt> 実行時ではなく、
   780  マクロ展開の時点で動き始めてしまって、おかしなことになるためです。
   781  色々考えた結果、とりあえずこの中途半端な混合が具合がよいのではないかということになりました。
   782  </p>
   783  ))
   784  $(SECTION 微妙なところ2, $(SECBODY
   785  <p>
   786  「関数実行開始時に、まずマクロレイヤを実行」と書きましたが、この時、関数内関数まで辿りにいくので、
   787  何重にもネストした関数を使っていると、内側の関数は、何重にもマクロ展開が走ってしまいます。
   788  これはなにかおかしい気がしますね。Scheme などはどうなっているのか調べないと…。
   789  </p>
   790  ))
   791  $(SECTION 微妙なところ3, $(SECBODY
   792  <p>
   793  これはエラーになります。
   794  </p>
   795  <pre>
   796      &gt;&gt; let _ = (@macro twice(x) {x;x} in twice(print("Hello")))
   797      polemy.failure.RuntimeException@C:\Develop\Projects\Polemy\polemy\value.d(109):
   798      [<REPL>:2:35] 'twice' is not set in @value layer
   799  </pre>
   800  <p>
   801  どういうことかというと、<tt>@macro</tt> で定義したマクロはいつから使えるようになるかという話で、
   802  この <tt>@macro twice(x) {x;x} in ...</tt> の部分は <tt>@value</tt> レイヤの式なので、
   803  まずこの式全体のマクロ展開が終わったあとにしか実行されないのです。<tt>twice</tt>
   804  がマクロと見なされはじめるのは、<tt>@macro</tt> 実行が終わった後。
   805  なので、
   806  例えば <tt>twice(print("Hello"))</tt> の部分を無名関数にラップしてやれば、
   807  マクロ展開を遅らせられて、 ちゃんと実行ができます。
   808  </p>
   809  <p>
   810  これだと余りにも不便なので、関数のトップレベルの変数宣言式の列についてだけは、
   811  <tt>@macro</tt> と <tt>@value</tt> の評価を交互にインターリーブするようにしました。
   812  「関数やREPLのトップレベルの最初に宣言したマクロだけは、その関数内で即座に使える」わけです。
   813  これも Scheme の let-syntax などなどの動きを調べて勉強しないと…。
   814  </p>
   815  ))
   816  )
   817  ))
   818  
   819  
   820  $(SECTION Built-in Primitives, $(SECBODY
   821  <p>
   822  組み込み関数・変数の一覧。
   823  </p>
   824  $(DDOC_MEMBERS
   825  
   826  $(SECTION テーブル操作, $(SECBODY
   827    $(TABLE
   828      $(TR $(TH {}) $(TD ()) $(TD 空のテーブルを作る))
   829      $(TR $(TH .) $(TD (t, s)) $(TD テーブル t の名前 s のフィールドの値を取得。なければ <tt>undefined</tt>))
   830      $(TR $(TH .?) $(TD (t, s)) $(TD テーブル t に名前 s のフィールドがあれば 1、なければ 0))
   831      $(TR $(TH .=) $(TD (t, s, v)) $(TD テーブル t を親に持ち、名前 s のフィールドに v が入ったテーブルを作る))
   832    )
   833  ))
   834  <br />
   835  
   836  $(SECTION 制御フロー, $(SECBODY
   837    $(TABLE
   838      $(TR $(TH if) $(TD (n, ft, fe)) $(TD n が非 0 なら <tt>ft()</t>、0 なら <tt>fe()</tt> を実行))
   839    )
   840  ))
   841  <br />
   842  
   843  $(SECTION 演算, $(SECBODY
   844    $(TABLE
   845      $(TR $(TH +) $(TD (n, m)) $(TD 整数 n と整数 m を足して返す))
   846      $(TR $(TH -) $(TD (n, m)) $(TD 整数の引き算))
   847      $(TR $(TH *) $(TD (n, m)) $(TD 整数の掛け算))
   848      $(TR $(TH /) $(TD (n, m)) $(TD 整数の割り算))
   849      $(TR $(TH %) $(TD (n, m)) $(TD 整数の剰余))
   850      $(TR $(TH &amp;&amp;) $(TD (n, m)) $(TD 整数 n と m が両方非 0 なら 1、それ以外では 0))
   851      $(TR $(TH ||) $(TD (n, m)) $(TD 整数 n と m がどちらか非 0 なら 1、それ以外では 0))
   852      $(TR $(TH ~) $(TD (a, b)) $(TD a と b を文字列化して結合))
   853      $(TR $(TH &lt;) $(TD (a, b)) $(TD a と b を比較))
   854      $(TR $(TH &lt;=) $(TD (a, b)) $(TD a と b を比較))
   855      $(TR $(TH &gt;) $(TD (a, b)) $(TD a と b を比較))
   856      $(TR $(TH &gt;=) $(TD (a, b)) $(TD a と b を比較))
   857      $(TR $(TH ==) $(TD (a, b)) $(TD a と b を比較))
   858      $(TR $(TH !=) $(TD (a, b)) $(TD a と b を比較))
   859    )
   860  <p>
   861  注意点として、作者の趣味の問題で、<tt>&amp;&amp;</tt> と <tt>||</tt> は short-circuit 評価をしません。
   862  整数演算の種類が少ないのは、D 言語の std.bigint がビット演算などをサポートしてないためです。
   863  文字列が結合しかできないのは、単に手抜きです。
   864  </p>
   865  ))
   866  
   867  $(SECTION 外部とのやりとり, $(SECBODY
   868    $(TABLE
   869      $(TR $(TH print) $(TD (a)) $(TD a を文字列化標準出力に改行付きで表示して、a を返す))
   870      $(TR $(TH argv) $(TD ) $(TD スクリプトに渡された引数文字列のconsリスト))
   871      $(TR $(TH gensym) $(TD ()) $(TD エセgensym。変数名として他とかぶらなそうな文字列を返します))
   872      $(TR $(TH rand) $(TD (n)) $(TD 0 以上 n 未満の自然数を31bit以内でランダムに生成します))
   873    )
   874  ))
   875  <br />
   876  
   877  $(SECTION データ型判定, $(SECBODY
   878    $(TABLE
   879      $(TR $(TH _isint) $(TD (a)) $(TD a が整数なら 1、でなければ 0))
   880      $(TR $(TH _isstr) $(TD (a)) $(TD a が文字列なら 1、でなければ 0))
   881      $(TR $(TH _isfun) $(TD (a)) $(TD a が関数なら 1、でなければ 0))
   882      $(TR $(TH _istbl) $(TD (a)) $(TD a がテーブルなら 1、でなければ 0))
   883      $(TR $(TH _isbot) $(TD (a)) $(TD a が未定義値なら 1、でなければ 0))
   884    )
   885  ))
   886  )
   887  ))
   888  
   889  )
   890  Macros:
   891      TITLE=Polemy Reference Manual
   892      DOCFILENAME=index.html
   893      SECTION=$(DDOC_DECL $(DDOC_PSYMBOL $1)) $(DDOC_DECL_DD $2)
   894      SECBODY=$0