Ribbitにdynamic-windとcall/ccを実装する

基本的にSchemeの継続があれば例外とかは実装できるから、これらを揃えれば後は eval
と read
だけってところだな。。

dynamic-wind
の実装方法
実はSchemeの現状の標準は、単純な call/cc
プリミティブだけではなく dynamic-wind
も実装することを求めている。
dynamic-wind
は
(dynamic-wind before thunk after)
のように、3つのパラメタ before
after
と thunk
を取る手続きで、
-
thunk
の中に入ったときはbefore
を実行する -
thunk
の外に出たときはafter
を実行する
ことを保証させる。もし、処理系が継続を実装していないならば、これは
(define (dynamic-wind before thunk after)
(define v #f)
(before)
(call-with-values thunk (lambda x (set! v x)))
(after)
(apply values v))
のように実装できる。 ...この段階で難しいじゃんという感じだが、 dynamic-wind
は thunk
が複数の値を返却した場合はその全てを dynamic-wind
の返却値とする必要がある。
C++における例外のような、脱出しか存在しない処理系では、脱出した際に after
を実行するロジックを用意する必要がある。例えば try
〜 catch
のようなプリミティブを使うことになるだろう。
Schemeにおける継続を使った場合、 thunk
の内部で継続を捕捉することによってその継続が起動される度に before
を実行する必要がある。これは C++ のような通常の言語では表現できない。例えば、KawaのようなこれをサポートしないScheme実装では before
は一度しか実行されない。
If the continuation of the
dynamic-wind
is re-entered (which is not yet possible in Kawa), thein-guard
is called again.
dynamic-wind
を知らない call/cc
を使って dynamic-wind
を実装する
TSPL4にそのものズバりの実装がある。
今回は、これをアレンジして移植している:
before
や after
をグローバル変数 %winders
に積んでおき、自分自身の積んだ手続きと %winders
が一致しない場合はそのぶんの呼出しが必要ということになるので呼出しを行う。
call-with-values
の代わりに以前Ribbitに追加した list->values
手続きを使用している。
exit
の処理
また、R7RSは、 exit
によってプログラムを終了するときも脱出と見做して after
を実行するように求めている。
今回はこの性質は無視している。

parameterize
の修正
dynamic-wind
があれば parameterize
も自然に実装できる。というかR7RSに実装も載っている。
... が、yuni側のコピペミスがあって動いてなかったのを修正した。
__1
や __2
はYuniの独自拡張で、この構文を使用すると内部で gensym
される。yuniはいわゆる define-macro
しか実装していないのでマクロが衛生的でない。このため、 syntax-rules
のユーザーは必要に応じて gensym
を手動で埋め込む必要がある。