Diff
Not logged in

Differences From Artifact [388d91fa67a7c403]:

To Artifact [33d07971634f6989]:


350 350 <pre> 351 351 &gt;&gt; @hoge( 1 + 2 ) 352 352 polemy.failure.RuntimeException@C:\Develop\Projects\Polemy\polemy\eval.d(466): 353 353 [<REPL>:3:7] only @value layer can call native function: + 354 354 [<REPL>:3:7] + 355 355 </pre> 356 356 <p> 357 -まだエラーですね。これは 実は、リフト関数は 357 +まだエラーですね。これは要するに "+" の意味がわからない、と言っています。 358 +$(RED $(B レイヤ指定変数定義式)) で、"+" の意味を教えてあげます。 358 359 </p> 359 360 <pre> 360 361 &gt;&gt; @hoge "+" = fun(x, y) {x} 361 -</pre> 362 -<pre> 363 -[Layers :: Overview] 364 - 365 - Polemy's runtime environment has many "layer"s. 366 - Usual execution run in the @value layer. 367 - 368 - >> 1 + 2 369 - 3 370 - >> @value( 1 + 2 ) 371 - 3 372 - 373 - Here you can see that @LayerName( Expression ) executes the inner Expression in 374 - the @LayerName layer. Other than @value, one other predefined layer exists: @macro. 375 - 376 - >> @macro( 1+2 ) 377 - {pos@value:{lineno@value:3, column@value:9, filename@value:<REPL>}, 378 - is@value:app, 379 - args@value:{car@value:{pos@value:{lineno@value:3, column@value:9, filename@value:<REPL>}, 380 - is@value:int, 381 - data@value:1}, 382 - cdr@value:{ 383 - car@value:{pos@value:{lineno@value:3, column@value:11, filename@value:<REPL>}, 384 - is@value:int, 385 - data@value:2}, 386 - cdr@value:{}}}, 387 - fun@value:{pos@value:{lineno@value:3, column@value:10, filename@value:<REPL>}, 388 - is@value:var, 389 - name@value:+}} 390 - 391 - (Sorry, this pretty printing is not available on the actual interpreter...) 392 - This evaluates the expression 1+2 in the @macro layer. In this layer, the meaning of 393 - the program is its abstract syntax tree. 394 - 395 - You can interleave layers. 396 - The root node of the abstract syntax tree is function "app"lication. 397 - 398 - >> @value(@macro( 1+2 ).is) 399 - app 400 - 401 - 402 - 403 -[Layers :: Defining a new layer] 404 - 405 - To define a new layer, you should first tell how to "lift" existing values two the new layer. 406 - Let us define the "@type" layer, where the meaning of programs is their static type. 407 - 408 - >> @@type = fun(x) { 409 - >> if( _isint(x) ) { "int" } else { 410 - >> if( _isfun(x) ) { x } else { "unknown" } } 411 - >> } 412 - (Note: polemy REPL may warn some exception here but please ignore) 413 - 414 - For simplicity, I here deal only with integers. 415 - _isint is a primitive function of Polemy that checks the dynamic type of a value. 416 - For function, leaving it untouched works well for almost all layers. 417 - 418 - >> @type( 1 ) 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 ) 419 530 int 420 - >> @type( 2 ) 531 + &gt;&gt; @type( 2 ) 421 532 int 422 - >> @type( "foo" ) 423 - unknown 424 - 425 - Fine! Let's try to type 1+2. 426 - 427 - >> @type( 1 + 2 ) 428 - ...\value.d(119): [<REPL>:6:8] only @value layer can call native function 429 - 430 - Note that the behavior of this program is 431 - - run 1+2 in the @type layer 432 - and NOT 433 - - run 1+2 in @value and obtain 3 and run 3 in the @type. 434 - The problem is, the variable "+" is defined only in the @value layer. 435 - To carry out computation in the @type layer. We need to define it also 436 - in the @type layer. 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 + )}; 437 553 438 - To define some variable in a specific layer, use @LayerName in place of 439 - (let|var|def)s. 440 - 441 - >> let x = 2 442 - >> @value x = 2 443 - >> @type x = "int" 444 - >> @hoge x = "fuga" 445 - 446 - For "+", do it like this. 447 - 448 - >> @type "+" = fun(x,y) {@value( 449 - >> if( @type(x)=="int" && @type(y)=="int" ) { "int" } else { "typeerror" } 450 - >> )} 451 - polemy.value.native!(IntValue,IntValue,IntValue).native.__anonclass24 452 - 453 - It is just computing the return type from the input type. 454 - Not here that the intended "meaning" of if-then-else is the runtime-branching, 455 - and the meaning of "==" is the value-comparison. These are the @value layer 456 - behavior. So we have defined the function body inside @value layer. 457 - But when we refer the variables x and y, we need its @type layer meaning. 458 - Hence we use @type() there. 459 - 460 - Now we get it. 461 - 462 - >> @type( 1 + 2 ) 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 ) 463 560 int 464 - 465 - Well, but do we have to define the @type layer meaning for every variables??? 466 - No. After you defined @type "+", you'll automatically get the following: 467 - 468 - >> def double(x) { x + x } 469 - (function:17e4740:1789720) 470 - 471 - >> @type( double(123) ) 472 - int 473 - 474 - Every user-defined functions are automatically "lift"ed to the appropriate layer. 475 - Only primitive functions like "+" requires @yourNewLayer annotation. 476 - 477 - 478 - 479 -[Layers :: neutral-layer] 480 - 481 - let|var|def is to define a variable in the "current" layer. 482 - Not necessary to the @value layer. 483 - 484 - >> @value( let x = 1 in @value(x) ) 485 - 1 486 - 487 - >> @macro( let x = 1 in @value(x) ) 488 - polemy.failure.RuntimeException: [<REPL>:14:29] variable x not found 489 - 490 - >> @macro( let x = 1 in @macro(x) ) 491 - {pos@value:{lineno@value:15, ... 492 - 493 - 494 - 495 -[Layers :: Layered-Parameters] 496 - 497 - >> def foo(x @macro @value) { {fst: x, snd: @macro(x)} } 498 - (function:1730360:1789720) 499 - 500 - If you annotate function parameters by @LayerNames, when you invoke the function... 501 - 502 - >> foo(1+2) 503 - {snd@value: {pos@value:{lineno@value:17, column@value:5, filename@value:<REPL>}, 504 - is@value:app, arg@value:{... 505 - /fst@value:3 506 - /} 507 - 508 - its corresponding arguments are evaluated in the layer and passed to it. 509 - If you specify multiple layers, the argument expression is run multiple times. 510 - If you do not specify any layer for a parameter, it works in the neutral layer. 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) { ...とても型を計算できないくらい複雑な定義... }; 511 604 </pre> 605 +<p> 606 +これが、レイヤ指定変数定義の典型的な使い道です。 607 +</p> 512 608 )) 513 609 ) 514 610 )) 515 611 516 612 517 613 $(SECTION Macro Layers, $(SECBODY 518 614 <p> 519 615 Polemy 言語組み込みのレイヤは <code>@value</code> と <code>@macro</code> の二つです。 520 616 (内部的にはもういくつかありますが、ユーザから直接は使えません。) 521 617 <code>@value</code> は、「普通に」普通のセマンティクスでプログラムを実行するレイヤでした。 522 618 <code>@macro</code> は、実は、<code>@value</code> よりも前に実行されるレイヤで、 523 619 「プログラムを実行するとその構文木を返す」というセマンティクスで動きます。 524 620 </p> 525 -<pre> 526 - (ここに例) 527 -</pre> 528 621 <p> 529 622 動きとしてはこうです。 530 623 </p> 531 624 <ol> 532 625 <li>関数呼び出し時(とトップレベル環境の実行開始時)に、 533 626 まず、<code>@macro</code> レイヤでコードを実行。</li> 534 627 <li>返ってきた構文木を、その関数を呼び出したときのレイヤで実行。</li>