Open3

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

okuokuokuoku

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

okuokuokuoku

dynamic-wind の実装方法

実はSchemeの現状の標準は、単純な call/cc プリミティブだけではなく dynamic-wind も実装することを求めている。

dynamic-wind

(dynamic-wind before thunk after)

のように、3つのパラメタ before afterthunk を取る手続きで、

  1. thunk の中に入ったときは before を実行する
  2. 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-windthunk が複数の値を返却した場合はその全てを dynamic-wind の返却値とする必要がある。

C++における例外のような、脱出しか存在しない処理系では、脱出した際に after を実行するロジックを用意する必要がある。例えば trycatch のようなプリミティブを使うことになるだろう。

Schemeにおける継続を使った場合、 thunk の内部で継続を捕捉することによってその継続が起動される度に before を実行する必要がある。これは C++ のような通常の言語では表現できない。例えば、KawaのようなこれをサポートしないScheme実装では before は一度しか実行されない。

https://www.gnu.org/software/kawa/Exceptions.html#idm45230722891456

If the continuation of the dynamic-wind is re-entered (which is not yet possible in Kawa), the in-guard is called again.

dynamic-wind を知らない call/cc を使って dynamic-wind を実装する

TSPL4にそのものズバりの実装がある。

https://www.scheme.com/tspl4/control.html

今回は、これをアレンジして移植している:

https://github.com/okuoku/yuniribbit-proto/blob/82b8adc81124b99bcd4bfac3971f6f64863de3b9/runtime/rvm/exception-runtime.sls

beforeafter をグローバル変数 %winders に積んでおき、自分自身の積んだ手続きと %winders が一致しない場合はそのぶんの呼出しが必要ということになるので呼出しを行う。

call-with-values の代わりに以前Ribbitに追加した list->values 手続きを使用している。

https://zenn.dev/okuoku/scraps/e7802d72241e98

exit の処理

また、R7RSは、 exit によってプログラムを終了するときも脱出と見做して after を実行するように求めている。

今回はこの性質は無視している。

okuokuokuoku

parameterize の修正

dynamic-wind があれば parameterize も自然に実装できる。というかR7RSに実装も載っている。

https://github.com/okuoku/yuni/blob/3d709003c611493be6fd476d4e7da607ef02bb0c/lib-r7c/r7c-report/misc/parameterize.sls#L33-L59

... が、yuni側のコピペミスがあって動いてなかったのを修正した。

__1__2 はYuniの独自拡張で、この構文を使用すると内部で gensym される。yuniはいわゆる define-macro しか実装していないのでマクロが衛生的でない。このため、 syntax-rules のユーザーは必要に応じて gensym を手動で埋め込む必要がある。