Continuations library for Java | Lambda the Ultimate
LtUより
JavaでContinuationサポートするライブラリたち。RIFEのものは、pauseとprintによって、それこそコンソールアプリとかHSPのようなインタラクションでもフローっぽい記述ができるようになっている。
一般的に、継続再開をどうやって実現化するかですが、continuationは「それ以降の処理を関数(オブジェクト)化」したものでもあるので、オブジェクト化と例外処理などを使ったバイトコード書き換えでできると思います。
pauseを使ったコード:
void init() { int a = 10; print(a); for (int i = 0; i < 10; i++) { a++; func(); print(a); } } void func() { int b = 20; print(b); pause(); b++; print(b); } init(); resume();
という記述から、Java上で中断再開化を実現するには:
void init() { new LocalCont() { int a; int i; int context = 0; void run1() { if (context == 0) { context = 1; a = 10; print(a); i = 0; } for (;i < 10 i++) { run2(); } context = 0; } void run2() { if (context == 1) { context = 2; a++; } run3(); context = 1; } void run3() { if (context == 2) { context = 3; if (hasSub(0)) { popSub(0).run1(); } else { try { func(); } catch (LocalCont c) { pushSub(0, c); throw this; } } } run4(); context = 2; } void run4() { if (context == 3) { context = 4; print(a); } context = 3; } }.run1(); } void func() { new LocalCont() { int b; int context = 0; void run1() { if (context == 0) { context = 1; b = 20; print(b); } run2(); context = 0; } void run2() { if (context == 1) { context = 2; throw this; } run3(); context = 1; } void run3() { if (context == 2) { context = 3; b++; print(b); } context = 2; } }.run(); } try { init(); } catch (LocalCont c) { c.run1(); }
こんな感じでバイトコードの書き換えをするのでしょう。つまり、ローカル変数、continuationが起こる可能性のあるコード位置の現在値を退避させてしまう感じです。やり方はC#2.0のyield returnとほぼ同じなんですが。
中断再開でも汎用的に行う実装をするのはそれなりに大変だろう。
まず起点からpauseまでのコールスタックはすべてこのように退避させることになるでしょう。それを行うには、クラスローディング時にクラスローダーに先んじてクラス解析+フロー分析するという方法もとれなくもない。しかし、コスト的にはそんなにメリットもなさそうなので、continuation対象クラスをpauseが可能なクラス内だけにして、そのクラスのメソッドのコールをすべてラップすることになるのではないだろうか。
本来のcontinuationは外側コンテキストからつかう再開よりは、内側コンテキストから使う大域脱出がメインかも。けどそれはJavaでは例外処理で可能なんであまり必要ないかもしれない。
Mono-1.1.9.1で自己パラメータ渡しのジェネリッククラス定義が可能になった
パラメータ型にサブクラスを指定するもの。それをインタフェースに出すためにキャストできるようにするもの。こういうの
public class ThisType<SubType> where SubType: ThisType<SubType> { public SubType Self() { return (SubType) this; } } public Sub : ThisType<Sub> { public string Hoge() { return "hoge"; } } public class Init { public static void Main() { Sub sub = new Sub(); Sub ref = sub.Self(); System.Console.WriteLine(ref.Hoge()); } }
1.1.8.3ではgmcsでのコンパイル自体だめだったけど、1.1.9.1ではコンパイル可能でうまく動くようになっている。