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* っぽい動きをします。