JavaScriptはもう好き嫌いを超えて、最低限の読み書きはもはや教養レベルといっても言い過ぎではないと思います。ブラウザ限定だったら他の言語もありますが、ブラウザで標準で使える言語はJavaScript以外には選択肢はありません。3DCG系のツールのマクロ言語は未だにPythonがトップシェアだと思いますが、Flash, Photoshop, Illustratorの仕事を効率化するマクロ言語はJavaScriptですよね。先日AppleのOS Xの次期バージョンの自動化ツールが独自言語に加えてJavaScriptをサポートすることを発表しました。サーバサイドで使われるnode.jsは、コンパイル言語を除けばトップクラスの性能です。QtもQMLとしてJavaScriptを中心とした開発を推し進めてますし、Qt4時代からQtアプリのマクロ言語として提供されているQtScriptEngineはJavaScirptです。node WebKitというGUI環境もあります。Cocos 2d-xのJSバインディングとかも情報が沢山出回ってますよね。.netも初期バージョンからJavaScriptのコンパイラがついてました。Unityで使えるという型付きJSはこれなのかな?ブラウザからデスクトップ、サーバ、スマートフォン、ゲームにいたるまで勢力を広げています。ハードウェアまで広がろうとしています。
で、JavaScriptというと話題によく出てくるのが非同期のプログラミングです。ブラウザやnode.jsでは、コールバックの嵐になるとよく言われたものです。それに対するソリューションはいくつかあり、僕が使ったことがあるのはasync.jsと、次期JavaScriptに入ることが確定しているPromiseです。
Futureはどこへ?
Promiseという言葉を聞いて、察しのいい人はJavaの標準ライブラリのFuture/Promiseを思い出すと思います。Future/Promiseを最初に考案したのは誰か、というのはよくわからなかったのですが、1976, 77年あたりからこの名前が使われ始めたとのことです。Promise pipelineといのが元々のアイディアで、ある意味、プログラミング依存関係のあるタスクに関する方程式みたいなものです。
アインシュタインの子供の頃の逸話で、技師をしているおじさんから「方程式とはずるい式だ。未知の値をわかったかのように記述して、あとからその未知の値を見つける」的なことを言われた、というのを昔、本で読んだことがありますが、Future/Promiseはそれと同じです。プログラミングの処理をするときに、事前に必要なデータがあったとします。今はそのデータは手元にないのですが、Futureというものがその値を知っているとみなして処理を書きます。Promiseはそれと対となる考え方で、その値を提供するという約束です。データが準備できたらPromiseに契約完了を伝えます。あとは、Promise pipelineという仕組みが依存関係をスマートに解決し、プログラムが引っかからないで実行できるようにする、という仕組みです。
Scalaなどは、Futureを取ってくるところまで処理したら、他のタスクに処理を回してPromiseが解決するまで待つみたいです。いまどきのプログラムは時間がかかるタスクというのはディスクアクセスだったり、データベースだったり、ネットワークで情報を取ってきたり、 外部の処理の完了を待つというのが大半です。Future/Promiseも非同期の待ち合わせなので、そのような待ち時間を無駄にしないための非同期の待ち合わせとも相性がよく、自然と統合できます。
ですが、JavaScriptのPromiseにはFutureというものは登場しません。JavaScriptのPromiseは、requireなどと同様に、サーバサイドのJavaScriptの仕様を統一して、既存の他の言語なみにJavaScriptを強力な言語にしようというCommonJSの活動の中で定義されたというのが僕の理解です。なぜJavaScriptにはFutureがないのかはきっとこの中にあるはず。さっそく、Promiseのトップページの履歴を見て、一番古いページをのぞいてみましょう。もう答えはわかりましたね。JavaScriptのPromiseの元は、Promise pipelineじゃないんです。Dojoのライブラリにあった、処理を遅延させるDeferredの名前が変わったものなのです。で、そのDeferredがどこから来たかというと、コメントによるとMochiKitとのこと。MochiKitはPythonにインスパイアされたライブラリで、prototype.jsが騒がれていた時代のライブラリです。Pythonでも有名なBob Ippolitoが開発したもので、Python製のウェブアプリケーションフレームワークのTurbo Gearsの最初のバージョンでバンドルされてました。そのMochiKitは、Pythonで大量の並列処理をさばくためのライブラリで、O'reilly本の表紙がキモイことで有名
なTwistedを元にしています。つまり、JavaScriptのPromiseの先祖はPromise pipelineではなく、PythonのTwistedだったとのこと。FutureがJavaScriptにない理由もこれで明確になりましたね。
厳密には、DeferredだったものはPromises/Aという名前のグループに属します。CommonJSの中でいくつかの種類があるのですが、最終的にJavaScriptにマージされたのはこれに少し機能が追加されたPromises/A+になります。
非同期の開発で大変なのは、たくさんの動きまわる非同期なタスクを制御すること
JavaScriptには、async.jsというライブラリもあり、これも非同期を扱いやすくするものです。ただし、引数の数がちょっと変わったとかの場合に関数オブジェクトのパラメータがずれて死亡みたいなケースが結構起きやすく、大規模になってくると結構厳しいです。あと、仕事コードはasync.jsからPromiseを使ったコードに書き換えている途中ですが、この2つの非同期タスク制御が混ざると結構つらい。Promiseのライブラリとしては、BlueBirdや、BlueBirdにいくつかasync.jsっぽい機能を足した、DeNA West製のThrushあたりを使うのがおすすめですよ。ネイティブのはまだ実装されて浅く、現時点では(node --harmonyとくらべて)ベンチマークを取るとBlueBirdの方が早いらしいです。これらには、nodeifyとかpromisifyといったメソッドがあって、Promiseに移行中で、コールバックベースのコードが残っている場合に、両方のAPIセットの橋渡しをしてくれます。まあ、Promiseの方も油断するとPromiseオブジェクトをreturnし忘れて非同期待ちされなくて、テストコードよりも先にユニットテストのafterブロックが先に実行されちゃって、sinonのスタブが解除されてテスト通らなく迷う、みたいなのがあったりはしますが、こちらの方がasync.jsよりも軽傷かな・・・
asyncはgoに移植されていたり、ともかく非同期を扱うのはやっぱり手続き型よりも面倒、いろいろ改善したい!と思っているプログラマが多いのが現状かと思います。そのgoも非同期界では最近ブイブイ言わせている言語の一つですが、非同期を大量に使うと、ちょっと上位でそれらを制御するスーパーバイザーが欲しいとか思ってしまうらしい(たしか@moriyoshi談)。非同期というと最終的にいつもラスボスのように出てくるErlang。Erlangは言語にメッセージパッシングが組み込まれていて、同じマシン内でも、外部のマシンでも同じように通信できます。関数型でよくあるパターンマッチと組み合わせてシンプルに表現できます。そして、OTPと呼ばれるライブラリで、スーパーバイザーを実現しています。そのErlangに関する600ページの分厚い翻訳書すごいErlangゆかいに学ぼう!
が出版されます。ちなみに、このエントリー、ここまでが前書きです。
すごいErlang
「Erlangはすごいのはよく聞くけど、Erlangはちょっと・・・」という人は多いと思います。Erlangというと、独特な(C++とかJavaとかLL系言語を使っていた人からすると)文法で知られます。非同期でプログラムの構造も大きく変わり、文法まで大きく変わるため、なかなか手が出ないのが実情かと思いますが、この本は手続き型の言語のバックグラウンドを持った人向けに丁寧に書かれていますし、翻訳もすごい丁寧です。以前のブログで、関数型を広めるには、関数型でしかできないこと、やりやすいことをPRしようと書きましたが、この本は「関数型ってよく聞くけど、よくわからないな」という人にも良いと思います。関数型のプログラミングを最初のステップにして、少しずつErlangっぽいプログラミングに近づいていける丁寧な本です。文法だけでなく、Erlangが持つ、さまざまな開発支援ツールまで網羅されています。
また、レビューア陣も、僕みたいな雑魚をのぞいて、日本のErlang界を代表するような方々です。僕は英語のO'reilly本と、飛行機本を片手にProject Eulerをやったりしたぐらいですが、他の人が進めているので筋がとても良い本のはずですし、僕が読んでもすごい理解しやすいと感じました。
ただし、分量があまりにも多く、次女の出産とレビュー時期が重なってしまい、最初の方しか読めなくて一部しか貢献できず、でした。すみませんでした。お詫びに娘の写真を一枚貼っておきますね。宣伝成分が少なめですが、世の中JavaScriptな人の方が人口は多いと思うので、PromiseにつられてErlangにも興味を持ってもらえたら良いなぁ、と思っています。あ、今月末で日本に帰任になります。サンフランシスコの方々、最後までバタバタしてたせいでご挨拶に伺えなくて申し訳ないです。まだコード書いてコミットしたりしています。仕事はいつ収まるのやら・・・
