内部defineの振舞い

本日のSICP読書会で出た話題。問題4.19。

ubuntu% cat test.scm 
(display
 (let ((a 1))
   (define (f x)
     (define b (+ a x))
     (define a 5)
     (+ a b))
   (f 10)))

(display "\n")
ubuntu% ./sscm test.scm 
16
ubuntu% gosh test.scm 
20
ubuntu% guile -s test.scm 
ERROR: Unbound variable: a
ubuntu% scm test.scm 

SCM Turtlegraphics Copyright (C) 1992 sjm@cc.tut.fi, jtl@cc.tut.fi
Type `(help-gr)' or `(help-turtlegr)' for a quick reference of
the new primitives.

;While loading "test.scm", line 7: ERROR: "test.scm", line 4: unbound variable:  a

原理的には内部定義は同時に起こる為、Gaucheの動きが正しい。しかしこれを満たす実装はなかなか大変である為、16を返すぐらいならunbound variable: aを返せとSICPには書いてある。確かにSigSchemeでこれに対応しようと思ったらパフォーマンス落ちそうだなぁ。どうしましょ。

Hanoi Counterが動かなくなった原因もこの問題に関連すると思われる。最初の(read)と2回目の(read)がコードの順番に実行されるという保証はどこにもない。原理的には"同時"なのだ。この場合はlet*を使用するのが無難ですな。