CPSチェイン

追記:「東京Node学園#1「非同期プログラミングの改善」のエッセンス」というスライドで、似たようなテクニックが使われているのを見た(p.32-)。 そんなにスジは悪くないのかもしれない。


JavaScriptでは非同期的に関数を呼ぶことが多い。例えばimgタグのロードが終わったら次にAを処理して、それが終わったらBをして…など。
こういうときCPS形式でプログラムを書くはめになる。

func1(arg1, function(res1){ func2(arg2, function(arg3, res2){..})})

かようにJavaScriptでは関数リテラルがすこぶる扱いづらい -- 'function' とか 'return' とか {} のネストとか…あとカリー化するにも一苦労 -- JavaScriptプログラマはどう書くんだろう? こういう非同期呼び出しをうまく同期的に記述するためのsyntax sugarがほしい。 第一級継続があればうまいこと書けるのだろうけど。
さしあたり function(引数,継続) {} という形の関数を逐次的に呼び出す関数を書いてみた。

function chain() {
  function cpsbody(a) {
    if(a.length<2) { return a[0]; }
    return a[1](a[0],function(r){return cpsbody([r].concat(a.slice(2)));})
  }
  var args = Array.prototype.slice.call(arguments); // making array from arguments
  return cpsbody(args);
}

こんな風に使う。

function f1(arg,ifdone) {
  return ifdone(arg+" f1 ");
}

function f2(arg,ifdone) {
  return ifdone(arg+" f2 ");
}

alert(chain("start",f1,f2,f1,f2)); // "start f1 f2 f1 f2 " がダイアログボックスに出る