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では例外処理で可能なんであまり必要ないかもしれない。