Let's start Scheme

2011-04-15

今更ながらにR6RSのライブラリについて

R6RSのライブラリについて大きな勘違いをしていたかもしれないということに気づいた。

現状Sagittariusにおけるライブラリの扱いはfirst class objectになっているのだが、これが元でsyntax-caseの実装が難航している感がある。
どういうことか?
僕がR6RSのライブラリを正しく理解しているかは分からないが、このライブラリシステム、Javaのクラスとかパッケージの感じではなくて、それ自体がマクロの一部として扱われた方がマクロを実装する際に煩雑さが消える気がしている。

実はこうではないかと思っている案
(library (something)
    (export do-something)
    (import (rnrs (6)))
  (define (print . args)
     (for-each (lambda (arg)
                  (display arg))
               args)
     (newline))
  (define (do-something . args)
     (print args)))

(import (something))
(do-something 1 2 3)
ライブラリがマクロであると考えると、これはこんな感じで展開されるはず
;; #: プレフィックスはrenameの結果とする
;; このプレフィックスはユーザが指定できないようR6RSでレキシカルエラーになるものがよい
;; print -> #:print
(define (#:print . args)
  ...) ;; 中身は一緒
;; do-somethingはexportされているのと、
;; 呼び出し元でprefixもしくはrenameで変更されていないのでこのまま。
(define (do-something . args)
  ...) ;; 中身は一緒
;; メインのプログラム
(do-something 1 2 3)
こう考えれば、ライブラリは展開時(読み込みとはもはや呼べないので敢えてこう言う)にrenameできるので、名前の衝突はなくなるし、C/C++のincludeより多少高等ではあるが、最終的に実行単位は1ファイルということになる。
また、R6RSでは定義の書き換えが禁止されているので(あんまりこれに追従してる処理系は多くないけど)importされた定義が書き換えられるということはない。

と、こう考えると、なぜimportレベルが与えられたのか分かる気がする。つまり、ライブラリを展開する際に先にimportしておかなければならないライブラリがどうしても必要になるからだ。たとえばsyntax-case内で使用される手続き。
これらの手続きは展開時に実行される必要があるので、import時にコンパイラもしくは実行する何かに教えてやる必要がある。曰く、この手続きはマクロ展開時に*実行*してね、と。単純に考えれば、マクロ展開器はevalを使う必要が出てくるということになる。
そもそも、importレベルはsyntax-caseのために導入されたのじゃないかと思うが。個人的にはcondition systemと一緒で言語仕様から切っても切れないものをライブラリと呼んではいけない気がする。evalは一応ライブラリか。しかし、Primitiveでないと実装できないようなものをライブラリと呼んでいいものか。
(error、とかassertion-violationなんてもろCondition Systemに依存してるくせに言語仕様の方に入ってるしなぁ。どうよ?)

さて、この考え方が正しいかどうかは別として(いろんなR6RSの処理系(といってもmosh、Ypsilon、petiteの3つだが)を見てると正しそうではあるが)、この方法で実装するとなると、現状のSagittariusを大幅に変える必要がでてくる。
そもそもライブラリなんていらんかったんだってことになるからだ。
ここまで作ってやり直しになるとさすがに凹むので、別の解を見つけたいところではあるが、いい案が思い浮かばない。

No comments:

Post a Comment