let と let* と letrec と letrec* の違い
#Lisp_Scheme で盛り上がった話。
皆さんは let と let* と letrec と letrec* の違いが説明できるでしょうか。僕は説明できませんでした。
letrec* については R6RS:翻訳:R6RS:11.4.6 Binding constructs あたりを読めば書いてあるのですが説明が難しいです。
もし間違っていたらごめんなさい。
やれることが大きいものから紹介。
letrec*
letrec* は束縛が左→右に参照できます。
(letrec* ([a b] ;; a の初期値として b を参照できる [b 3]) a) => 3と思ったらエラーらしい by lequeさん (letrec* ([a 3] [b a]) ;; b の初期値として a を参照できる b) => 3
letrec
相互に参照できますが、初期値の設定時に評価されてはいけません。
(letrec* ([a b] ;; a の初期値として b を参照すると b が評価されてしまうのでエラー。 [b 3]) a) => エラー
値が評価されないように lambda でくるんであげればセーフ
(letrec* ([a (lambda () b)] ;; b を参照するが値は評価していない [b 3]) a) => #<closure>
let*
一方向にしか参照できない。
(let* ([a b] ;; a の初期値として b を参照できない [b 3]) a) => エラー
これは OK。
(let* ([a 3] [b a]) ;; b の初期値として a を参照できる b) => 3
let
ご存じ let 。
(let ([a 3] [b a]) ;; b の初期値として a を参照できることはできない b) => エラー
おまけ
これでやっとGaucheの letrec は letrec*?の議論も理解できます。
gosh> (letrec ([a b] [b 3]) a) 3
letrec* っぽい動きですね。 letrec も letrec* もこれはエラーにならないといけない気がする。
R5RS的には未定義動作かな。
ちなみに Mosh も letrec* っぽい動きをします。