Island Life

< 藻から油 | 年度内感覚 >

2011/05/09

let地獄

OCaml の let と let rec はなぜ別扱いになっているのか、決定版、もしくは OCaml 暦十何年だったか忘れたけど仕事で Haskell を一年使ってみた

Schemeなんかもっとヒドい。letlet*letrecがあったところに最近letrec*が増えた。そのうえマクロだがand-let*があったりlet-valuesがあったりlet1があったりrlet1があったり。誰だSchemeはシンプルな言語だなんて言った奴は。

まあ、本質的に必要なのはletletrecだけで、それを統合できない(したくない)理由はOCamlのそれとほとんど同じ。もうひとつScheme特有な事情を挙げると、マクロがあるために「変数を確実にシャドウする手段」が必要ってことがある。 (aif <test> <then> <else>) で、<test>を評価して真だったらその値を暗黙の変数itに束縛して<then>を評価する、なんて非健全なマクロを書いた場合、その展開は束縛フォームを挿入するが (簡略のためlegacy macroな表記で):

 `(let ((it ,<test>))
    (if it ,<then> ,<else>))

<test>の中にitが含まれていたら、それは確実に外側のitを参照してもらわないと困る。「再帰参照があったらletrecとみなす」みたいに勝手に切り替わっちゃうとまずい。

let* に関しては、意味的には単なるネストしたletの構文糖衣であって、OCamlみたいにネストしたletをインデントを深くせずに書けたのなら不要だったろう。 let* の存在理由って、letを重ねるとインデントが深くなりすぎるってだけだもの。

letrec* の立ち位置は微妙。機能としてletrec*letrecの厳密バージョンであって、正しいプログラムであればその中のletrecletrec*に無条件に置き換えても動作する。だから、本来ならば、letrecの定義を現在のletrec*のように修正した方が綺麗だったんじゃないかと思われる。

そうした場合、「letで評価順が決まってないのにletrecでは決まることになり不自然だ」とも考えられなくはないが、そもそもletlambda式のごく単純な構文糖衣であって、その評価順が決まっていないのは関数適用で引数の評価順序が決まっていないからだ。letrecはそれが単純な構文糖衣であるような該当するナニカは無いので、どこかに義理立てする必要もない。letrecのひとつの定義は最初にローカル変数を全部未定義値に束縛してからset!してゆく、というものだが、その定義を使ったら自然に評価順も決まってしまう。

R6RSでletrec*が導入された経緯って何だったかなあ。

Tags: Programming, Scheme

Post a comment

Name: