展開のフェーズの話。(というよりは依存関係か)
たとえばこのコード
(library (my-lib) (export sub) (import (rnrs)) (define (bigger? x) (or (> x test))) (define-syntax sub (lambda (x) (syntax-case x () ((_ a b) (or (bigger? (syntax->datum #'a)) (syntax (let ((x b)) (- x 10)))))))) (let () (sub 10 x)))このコード、一見何もないように見えるけど、走らせると死ぬ。
一見何もないように見えるという段階でR6RSのライブラリについて理解してないのだが・・・orz
問題は、subの中で使われているbigger?がフェーズ0でしか参照されないこと。
subが展開されるのはひとつ前のフェーズ1なので、単純にunbound variableな例外を投げてくる。
これを解決するには、bigger?を別ライブラリにして、expandで読み込む必要がある。
まさにこれにはまっていて、
自前実装のコンパイラにライブラリを実装して、R6RSのライブラリにコンバートしてコンパイルなんてことをやっているのだが、
依存関係の解決が面倒くさすぎる。
たとえば、こんなコード
(define something 10) (define-macro (generate) `(make-vector ,(- something 5))) (define generated (generate))こんな感じのをライブラリにするとなると、somethingは外出しにしないと上記の理由で死ぬ。
というか、すでに死んでいる・・・
(逆に考えれば、意外と適当に実装したのに、その辺もうまいこと動いているということだが・・・)
これはライブラリの仕組みが悪いのか、Schemeにマクロがあるのが悪いのか(そもそも良い悪いの問題ではないと思うが)
わからないけど、あまり相性が良くない気がする。
プログラマがいろいろなことを気をつければいいのだけど、R5RSで書いてて、R6RSにコンバートするって時に(まさに今)非常に不便・・・
2 comments:
状況が許すなら internal define を使うのが最も簡単でポータブルです。
あと、処理系によるのですが、ひとつのファイルにふたつ以上の library を書くことを許している場合があるので、それを使えば若干楽に書けるかと思います。 確か petite chez scheme はそうだったように記憶しています。 (すいません。 うろおぼえです。) library をどのように格納するかは r6rs では明確にしておらず、必ずしもファイルに対応付けなくてもよいので、フェイズの概念と実用の妥協点としてはこの方式が地味に便利だと個人的には感じています。
internal defineも確かに一つの手ですね。
残念ながら、同じ手続き(と呼ぶと関数型言語っぽくないなぁ)を複数箇所で使いまわしてるので、今回はだめですが・・・
petite chez scheme、Ypsilon、nmosh(通常のmoshでない方)はlibrary複数格納可能でした。(確認済み)
多分psyntaxの関係なのか、通常moshは不可能っぽいです。
iron scheme、ikarusはCygwinでインストールしようとして失敗したので未確認^^;
1ファイル複数libraryだと、外部公開したくないけど、フェーズの関係で別libraryにする必要がある場合なんかは便利かなと思います。実際そんな状況がたくさんあるので・・・
Post a Comment