Future::Qアップデートと最近のPerlにおけるFuture/Promise/Deferred事情

ここ1,2週間でCPANモジュールFuture::Qを久々に更新した。

v0.050 → v0.090の主な更新内容は以下の通り。

  • resolve()メソッドを追加
  • finally()メソッドを追加
  • $OnErrorパッケージ変数で未処理のfailed Futureのエラーメッセージを受けるコールバックを登録できるようにした。

これでもう、機能的にはQと遜色ないレベルになったんじゃないだろうか。気が向いたら$OnErrorに文字列ではなくて(stringificationをオーバーロードした)オブジェクトを渡すように変えるかもしれない。

Future::Qを初公開したのは去年(2013年)の4月だけど、その頃から比べるとFuture周りのCPANモジュールはいろいろ事情が変わった気がする。


2013年4月当時、CPANにあるまともなfuture実装は自分の知る限りFuturePromisesくらいだった*1。ただし、Promisesの挙動はQではなくjQuery.Deferredをエミュレートしており*2、また、Futureにはthenメソッドがなかった。

自分はQと同様にthenメソッドを呼ぶことができ、Qと同様の挙動をするモジュールが欲しかったので、Futureを拡張してFuture::Qを作った。

しかし2014年3月現在、だいぶ状況は変わっている。

まず、現在のPromises 0.91はjQuery.DeferredではなくQに似た挙動をするよう、変更されているようだ。個人的にはこれはかなり豪快に後方互換性を壊す変更だと思うのだが、Promisesの作者はこれを断行したらしい。

また、現在のFuture 0.25はthenメソッドを備える。ただしFutureのthenメソッドはコールバックが必ずFutureオブジェクトを返さないといけないなど、ユーザに課す制約が厳しい。Haskellなどと違い、Perlではこの制約は実行時に評価されるため、非常に使いづらいと思う。

これら二つに加え、さらに新しいfuture系モジュールも登場している。

AnyEvent::PromisesはPromisesのフォークである。このモジュールはAnyEventと密結合するかわりに、thenコールバックが遅延実行されることを保証する。個人的にはこの保証がどうしても必要になる状況が分からない*3が、たしかにそのほうがよりQに近い挙動となる。

ただし、最新のPromisesではAnyEventやMojo::IOLoopといったイベントループを使うオプションがあるため、もうAnyEvent::Promisesは用無しになっているようにも思える。

そして先日、AnyEvent::Promiseという目を疑うような名前のモジュールがリリースされた。Promisesではなく、Promiseである。GitHubには2013年8月から存在していたようだが、CPANリリースはごく最近だと思う。

中身をざっと見ると、ある程度AnyEventのcondition variableと密結合した作りになっているようだ。しかしPromises/A+とも微妙にAPIが異なり、意図がよく分からない。まあ使ってみないことには理解できないだろう。

しかしなぜこんな名前でリリースしてしまうのか。事前に既存モジュールの調査をしないのか。そもそもPromisesやAnyEvent::Promisesではダメなのか。

とはいえ、futureの挙動はプログラムの実行の流れを支配するので、その仕様は非常に重要である。そんな重要なものを他人の書いたモジュールに任せられないという気持ちはまあ、理解できる。実際、Future::QもthenメソッドはFutureのものを使わない独自実装であり、綿密なテストを通している。

*1:Data::Monad::CondVarもあったが、当時はモナドの概念がさっぱり分からなかった

*2:QとjQuery.Deferredの挙動の違いについてはQのWikiが詳しい。

*3:Deep Recursionを防ぎたかったらCPSを使えば大抵問題ない