yuniではfull-continuationの提供を止めることにした。単純な理由はKawaがこれを提供していないことだが、じゃあこれに代えるプリミティブを何にするのかという点がちょっと悩ましい。

call/ccに対する批判はOlegのページが詳しい( http://okmij.org/ftp/continuations/against-callcc.html )。要するに現実的なプログラムで使うのは難しいという話で、実際継続を介したポートI/OやFFIは難しい。yuniやその前身で書かれたSchemeプログラムはそれなりの量が有るが、全てone-shotな継続しか使用しておらず、full-continuationが真に必要なケースは無い。

Rubyはcall/ccを1.9でContinuationライブラリに移し、

と地獄のようなことが書かれている(Fiberって継続の替わりになるの？)。

yuniも同様にcall/ccを(yuni continuation)あたりのライブラリに追い出してオプショナルにすることを考える。ただ、今のところ、yuniがサポートするScheme処理系で"call/ccが無いことを前提にパフォーマンスメリットを提供している処理系"は存在しないようだ。

with-exception-handlerとguardのセマンティクス

当のKawaはcall/ccをJavaの例外で実装している。

Kawa continuations are implemented using Java exceptions, and can be used to prematurely exit (throw), but not to implement co-routines (which should use threads anyway).

また、KawaはguardなどのSchemeの例外機構をJavaの例外機構にマップしている。

The Scheme exception model is implemented on top of the Java VM’s native exception model where the only objects that can be thrown are instances of java.lang.Throwable. Kawa also provides direct access to this native model, as well as older Scheme exception models.

実際、C++やJavaScriptのように言語のコア機能として例外機構を実装しているケースは多いため、これらと良くマッチするならばSchemeの例外機構はプリミティブとして適切だろう。ただちょっと難しいのは、Schemeの例外機構のセマンティクスをより理解して進めないと危ない気がしている。...つまりyuniの場合10近くあるScheme処理系でちゃんと挙動が一致するのかどうかを比較的真面目に気にする必要が出てくることになる。

(正直、Schemeの例外手続きの使いかたを覚えるのが面倒だからcall/ccで書かれているコードがそれなりにあるので、それらをguardなりwith-exception-handlerなりに書き換えたときに挙動を変えない自信が現状あんまり無い。)

Kawaの場合、guardはwith-excpetion-handlerより制約のあるプリミティブで、guardの方が効率的としている:

Performance note: Using guard is moderately efficient: there is some overhead compared to using native exception handling, but both the body and the handlers in the cond-clause are inlined.

(with-exception-handlerはlambdaの分のコストがある -- はずなんだけど上手いこと逆コンパイルできなかったので調査できていない。ToDo。)

ただ、通常の処理系ではwith-exception-handlerをプリミティブとして使用している。例えば、Gaucheのpull request https://github.com/shirok/Gauche/pull/335 は興味深い調整で、Gaucheに元から有ったSRFI-18の with-exception-handler とR7RSの同名手続きの挙動差への言及がある。

guardとwith-exception-handlerはどちらも例外ハンドラを表現するが、その差については最終的なコミットのドキュメントに要約されている:

(SRFI-18のwith-exception-handlerについて、)

例外がraiseやerrorで通知されると、投げられたコンディションを引数としてhandlerが呼び出し元と全く同じ動的環境で呼び出されます。つまり、handler中でraiseを呼び出すと、再びhandlerが呼ばれます。また、handlerから戻ると、制御はraiseの呼び出し元に戻ります。 この振る舞いはSRFI-18により定義されました。これは、この手続きが例外制御の最もプリミティブな構成要素になることを意図しています。例外処理中にアクティブなハンドラを切り替えたければ、自分でそう書く必要があります。 通常、例外処理中に例外が発生したら、それは「外側の」ハンドラで処理したいでしょう。そういった典型的な使い方には、guardを使ってください。この手続きはあくまで、例外処理の最も低層にアクセスしたい時のみ使うとよいでしょう。 R7RSにも同名の手続きがありますが、一つだけ違いがあります。R7RS版はhandlerを呼び出す前に現在の例外ハンドラを一つ「外側」の例外ハンドラに置き換えます。R7RSのwith-exception-handlerの説明はR7RS base libraryを参照してください。

このR7RS(= SRFI-34)とSRFI-18の差は https://github.com/shirok/Gauche/commit/234d0ef154ade1983283ece93f52d462d7833ca4#diff-fb36b06efa4fdb8b419faa6d294f0e0aR350 のコードでSRFI-18 with-exception-handlerを元にr7rs:with-exception-handlerを定義することで吸収している。

Gaucheでは例外ハンドラを戻す挙動をwith-exception-handler内に実装しているが、SRFI-34ではraiseの内部に実装しているという微妙な違いがある。