define の実装方法に関する考察 - Scheme VM を書く

以下の2例に関して define の実装を考える。

;; パターン A
(define a 3)
((lambda () a))

;; パターン B
(define b (lambda () c))
(define c 4)
(b)

define はトップレベルでのみ有効とし2つ実装方法を考えてみる。

global 変数は global に管理する

ヒープベースの VM の欠点は各フレームの env をさかのぼって変数の lookup をすることのオーバーヘッドが大きい点。
この問題はスタックベースになり display closure の仕組みを導入したことで解決された。(コンパイル時に変数の参照位置が解決されている。)
global な変数だけは動的に通常の lookup を行って良いのではなかろうかというのがこの方法。

例えばパターン A における define は以下のようにコンパイルされる。 a はシンボル。

'(const 3 (define-global a (next)))

そして参照するコードは

'(refer-global a (next))

となり参照結果が accumulator にセットされる。

この場合、内部実装はグローバルなハッシュテーブルに対する put/get になる。
コンパイラは display closure を考えた場合の free variable、そして local variable 以外のシンボルを refer-global として扱えば良い。
パターン B でもこの方法で実現できる。

display closure を利用する

display closure は、そのクロージャから見て local でない変数を保持しているものなのだからこれを利用しようというアイデア
前提は

の2点。

パターンAの場合
初期状態の display closure を

closure 組込み関数A 組込み関数B

とする。
define の登場で

closure 組込み関数A 組込み関数B aを保持(3)

となる。

次に a を参照する場面では (refer-free 2 (next)) とコンパイルされていれば参照は解決する。
ここで問題になるのは、あらかじめコンパイラが (refer-free 2 (next)) を生成できるか?につきる。
これは無理だよ。
repl や load で後からコードが動的に追加されるので厳しい。

結論

「global 変数は global に管理する」を採用する事としよう。