with-exception-handler の仕様を正しく理解しよう その3
仕様の理解できない部分は実装例を勉強することで分かるかもしれないので先に進む。
Mosh に足りなかったもの
- call/cc と動的環境の関係が理解できていない
- call/cc で capture/restore される環境が足りていない
- 動的存続期間が意識されて実装されていない
- これは dynamic-wind が実装されていないことも起因しているだろう
- C的 longjmp/setjmp と call/cc の capture/restore による脱出の関連を考察していない
仕様を満たしているか?
仕様を満たしているか意地悪なテスト方法を考える。
- with-exception-handler の handler 内で restore した場合に current-exception-handler はどうなるか?
- current-exception-handler も restore されるのが正解
- dynamic-wind をいくつも呼び出した場合に before / after は正しい順序で呼ばれるか。
Gauche の with-exception-handler の実装を見る
コードを読んでいて親しみやすい Gauche の実装を見てみよう。
src/vm.c あたりに ScmObj Scm_VMWithExceptionHandler(ScmObj handler, ScmObj thunk) がある。
ScmObj Scm_VMWithExceptionHandler(ScmObj handler, ScmObj thunk) { ScmObj current = theVM->exceptionHandler; ScmObj before = Scm_MakeSubr(install_xhandler, handler, 0, 0, SCM_FALSE); ScmObj after = Scm_MakeSubr(install_xhandler, current, 0, 0, SCM_FALSE); return Scm_VMDynamicWind(before, thunk, after); }
ふむ。Scm_VMDynamicWind が分からないとだめだな。
お。Exception handling に関する長文英語コメント発見。Gauche はコメントが多くて偉いといつも思う。
概念的には、例外ハンドリングは dynamic-wind と call/cc の特殊な組み合わせにすぎない。 Gauche ではその例外ハンドリングの仕組みの一部を、効率よく実行され安全に使えるようにCで実装している。
ってことは、Pure Scheme でもいけるのかな?(ここは保留)。
この仕組みは with-exception-handler と raise の2つから成るよ。
srfi-18 (multithreads) と srfi-34 (exception handling) では raise の意味論に関して意見が違うよ。
srfi-18: handler は例外を raise した動的環境と同じ動的環境で call しないといけない。 つまり handler が実行されているときの current-exception-handler は handler 自身。 なので handler の中で raiseすると無限ループになることもあるよね。
srfi-34: handler は raise と同じ動的環境で call される。 ただし、current-exception-handler が pop されたときはその限りではない。 すなわち exception handler が実行されていて、current-exception-handler は "外側の" もしくは "古い”handler の場合である。 handler 内で raise した場合、外の handler に制御がうつる。
お。これはひょっとしたらさっき分からなかったあれかな。
続く。