2012/09/01
再定義のセマンティクス
これコンパイルエラーにした方がいろいろ嬉しいのだが、それをやると非常に不便になる。以下が不便になる例。
(library (test) (export variable) (import (rnrs)) (define variable 1) ) (library (test2) (export variable) (import (rnrs) (test)) (define variable 'hoge) ; ここ (define (aa) (set! variable 2)) )R6RSやR7RS的にはコメントで示している部分はエラーにならないといけないんだけど、それやるとSRFIとかでガチンコでぶつかっている手続きがエラーになる。(removeとか、振る舞いまで違うから再定義せざるを得ない)。
まあR6RS的には、test2で (import (except (test) variable))
と書きなさいってことなんだけど、それはなぜか、というと、「importは何をしているのか」
という話になる。
R6RSのimportでは「識別子」というモノがあって、importはそれを 現在の名前空間に持ってくる。test2でtestをそのままimportしたら、 test2内でvariableという名前が意味するモノは、 testが意味するモノとまったく同じモノだ。
そして、Scheme世界全体として見た時に、 (単一フェーズで)おなじ「モノ」が二つの意味を持ってはいけない。
上の例で言えば、
(library (test3) (import (rnrs) (test)) variable)
とした時に、
これはいくつになるべきか、という話になる。
このvariableが指す「モノ」はtestで定義されたvariableの指すモノと同じで、
それはtest2にもimportされてるからtest2でのvariableが指すモノとも同じでなければならない。
つまり、test3がtest2を参照してなかろうが、test2が実行された時点で
前文の「モノ」は全て同時に再定義されることになる。
Common Lispではシンボルが上述の「モノ」の役割を担っていて、 しかも再定義が許されているので、test2を実行(ロード)した段階でvariableの意味が変わる。 testしかimportしていないtest3にとっては、 test2のロード前と後でvariableの値が変わるように見える。
一方、Gaucheでは、importは可視性をコントロールする。importしても、 現在の名前空間に何かが「持ち込まれる」ということはない。 単に、「名前を解決する時に探す場所」が付加されるだけだ。 このセマンティクスでは、test2の中でvariableを定義すると、 それはモジュールtest2内に束縛を追加し、test1内の束縛をシャドウする。 (つまり、独立した定義の追加であって再定義ではない)。 この場合、test3から見えるvariableは依然としてtest1のものなので、 その値は変わらない。
実用上、これは大抵の場面で期待した動作をしてくれる。 例えばライブラリが刻々と拡張されてゆく場合、次のような状況が起き得る。
- mylibraryがyourlibraryをimport
- mylibraryでfooを定義
- yourlibaryの新バージョンがfooを定義してexport
R6RSセマンティクスでは、yourlibraryの新バージョンを使う時に
mylibraryのimport節を (import (except (yourlibrary) foo))
に
書き直さなければならない。
R6RS的には、新たな識別子がexportされるっていうのはAPIの変更なんだから それを使ってるモジュールに影響が出るのは当然って立場で、 それはそれで筋が通ってはいる。R6RSの世界観っていうのは「一貫した静的な世界が まずあって、変更はイレギュラーな要因」なんだよな。
Gaucheは「世界とは刻々変わってゆくもので、 一貫性はもともと部分的にしか保証できないが、 「ここまではだいたい保証できる」って範囲をできるだけ明らかにしとこう」って立場。
(ただまあ、R6RSでも「importしてる識別子を再定義したら自動的にexceptされたことにする」ってのは不可能ではない。R6RS制定時に何かそういう議論があったような気もする。 libraryフォームはマクロ展開前に解釈されなくちゃならなくて、defineされてるかどうかは マクロ展開後にしかわからないから、ちゃんとした意味を決めるのはひどく面倒になるが、 マクロ展開後にimportが差し替えられるとみなす、とか妥協すれば実装は可能だ。)
Tags: Programming, Gauche, Scheme, CommonLisp
Post a comment