defineな疑問

なぜ関数の先頭じゃないとdefine出来ないのかイマイチわからない。

Gauche

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以外で試してみよう。

ゴニョゴニョ。

JavaScript
js> function f() { g(); function g() {} }
js> f()

あ。

Ruby(irb)
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で試す

エラーチェックの無い僕の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はどうなってるのか!?という新たな疑問が生まれてしまった。それはまた今度ということで。