なぜ関数の先頭じゃないとdefine出来ないのかイマイチわからない。
gosh> (lambda () 1 (define x 2))
*** ERROR: Compile Error: syntax-error: the form can appear only in the toplevel: (define x 2)
「only in the toplevel」トップレベルだけ。何故だろう。謎を追ってみる。
他の言語で試してみた
そうだ、Scheme以外で試してみよう。
ゴニョゴニョ。
js> function f() { g(); function g() {} }
js> f()
あ。
irb(main):001:0> def f() { g(); def g() {} }
irb(main):002:2> f()
そうか。
わかった
SchemeではJSやRubyのような事が出来ない。
gosh> (lambda () (g) (define (g)))
*** ERROR: Compile Error: syntax-error: the form can appear only in the toplevel: (define (g))
Schemeでは定義される前の関数を呼び出す事は絶対出来ない。S式を読みながら評価していくからだ。
確信へ
問題は、この時だ。
gosh> ((lambda () (define (f) x) (define x 1) (f)))
1
これはOK。fは、xを参照出来る。
エラーチェックの無い僕のSchemeで試そう。fを呼び出す位置を変えてみる。xが定義される前にfを呼んでみよう。
input> ((lambda () (define (f) x) (f) (define x 1)))
ERROR : eval : undefind value
xは定義されてないから、xは参照出来ない。というエラー。
もしもxがグローバル変数だったとき、思わぬバグを招くことになる。恐いデス。
つまり
Schemeでは「定義される前の値を参照することが出来ない」。関数内の値が先頭(トップレベル)で定義済みでないと、名前解決が出来ない。だから、関数の先頭に定義を書かなければならない。
JSやRubyとは関数生成方法が微妙に違うということがわかった。
JSやRubyはどうなってるのか!?という新たな疑問が生まれてしまった。それはまた今度ということで。