2011/05/09
let地獄
OCaml の let と let rec はなぜ別扱いになっているのか、決定版、もしくは OCaml 暦十何年だったか忘れたけど仕事で Haskell を一年使ってみた
Schemeなんかもっとヒドい。let
とlet*
とletrec
があったところに最近letrec*
が増えた。そのうえマクロだがand-let*
があったりlet-values
があったりlet1
があったりrlet1
があったり。誰だSchemeはシンプルな言語だなんて言った奴は。
まあ、本質的に必要なのはlet
とletrec
だけで、それを統合できない(したくない)理由は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
の厳密バージョンであって、正しいプログラムであればその中のletrec
をletrec*
に無条件に置き換えても動作する。だから、本来ならば、letrec
の定義を現在のletrec*
のように修正した方が綺麗だったんじゃないかと思われる。
そうした場合、「let
で評価順が決まってないのにletrec
では決まることになり不自然だ」とも考えられなくはないが、そもそもlet
はlambda
式のごく単純な構文糖衣であって、その評価順が決まっていないのは関数適用で引数の評価順序が決まっていないからだ。letrec
はそれが単純な構文糖衣であるような該当するナニカは無いので、どこかに義理立てする必要もない。letrec
のひとつの定義は最初にローカル変数を全部未定義値に束縛してからset!
してゆく、というものだが、その定義を使ったら自然に評価順も決まってしまう。
R6RSでletrec*
が導入された経緯って何だったかなあ。
Tags: Programming, Scheme
Post a comment