収拾付かないFactor入門も17番目。まだまだ全体の景色が見えないので、まとめにははいりませんよ。今日はtop level formsをしっかり理解して帰ってください(オレが)。

top level formsとは、ソースコードのうちdefinitions以外の部分です。どこに書いてあっても、ソースコードすべてがパースされた「後」に実行されます。試してみましょう。

こういうソースファイルを書きます。fooを定義する前にfooを呼ぶコードです。

IN: test
USING: io ;
foo
: foo ( -- ) "test:foo called." print ;

実行すると

P" temp.factor"
4: foo
^
No word named ``foo'' found in current vocabulary search path

パース時のエラーになってます。パースして、コンパイルされて、それから実行されるのですね、たぶん。

ではこういうのはどうでしょうか。

USING: io math prettyprint kernel ;
IN: test
3 4 + .
: + ( x y -- ) 2drop "test:+ called. dropped values! HAHAHA" ;

こいつを実行すると

7

普通にmathの+が呼ばれます。サーチの順序ではtestが先に入っていますが、パース時点ではまたtest:+が定義されていないからですね。そういうときには、DEFER:で、先にwordの空定義を作っておくことができます。Pascalのforwardみたいなもんですが、FactorのDEFER:では、呼ぶとエラーになるwordがまず定義されます。

まずは、DEFER:で+を宣言して、しかし定義はしないコードを書いてみます。

USING: io math prettyprint kernel ;
IN: test
DEFER: +
3 4 + .

実行結果。

Calling a deferred word before it has been defined

DEFER:した+がまだ定義されていない、と怒られました。

+の呼び出しの「後」に、vocaburary "test"の+を定義して、実行してみましょう。

USING: io math prettyprint kernel ;
IN: test
DEFER: +
3 4 + .
: + ( x y -- str ) 2drop "test:+ called. dropped the values! HAHAHA"  ;
"test:+ called. dropped the values! HAHAHA"

ということで、test内で+を定義したのより後にtop level formが実行されていることが確認できました。

ドキュメントには、前回疑問に思った"ソースファイル中でinがとれない"ということについても書いてありました。

Top-level forms do not have access to the in and use variables that were set at parse time, nor do they run inside with-compilation-unit; so meta-programming might require extra work in a top-level form compared with a parsing word.

さらに、変数スコープもtop-levelではダイナミックなあたらしいものが生成されるそうで、実行後にそのスコープはなくなってしまうそうな。このあたりの詳細は、スコープのところをやるときにつっこんでみることにします。