brook の実装を数学的に理解した(ような気がする)。
昨日書いた記事( brook の実装読んでみた - 人生ずっと勉強) に、id:hirokidaichi さんから
こちらもぜひ参考に http://www.slideshare.net/hirokidaichi/brook-10690678
というコメントをいただきました。
(コメントの引用ってもっとスマートにできないのかな・・・コピペしたけど)。
スライドの中で、「合成を定義すればよい」とあって(12枚目)、
「関数を合成・・・合成・・・合成関数・・・なんかどっかで聞いたことあんな」
と思ってぐぐってきたら、確かに高校か大学かどっかでやったような気がする。
合成関数 によると、
2つの関数 f(x) と g(x) があるとき、f(x) の値域が g(x) の定義域に含まれているとすると、
2つの関数は g( f(x) ) っていう、x に関する 1 つの関数に合成できちゃいまっせ、っていう感じらしい。
つまり、
function first ( param ) { return param + 1; } function second ( numCalculatedByFirst ) { alert( numCalculatedByFirst ); } var num = first( 1 ); // 関数 first の値域が、 second( num ); // 関数 second の定義域に含まれている
みたいなコードは、
second( first( 1 ) );
って合成できちゃうぜ、っていう話ですかね(違ったらすいません)。よく見かけるコードです。
ただ、ajax などで非同期処理をしたい場合は、すぐさま return することができない。
なので上記のような書き方はできない。
そこで、しゃーないから callback 関数を渡し、callback 関数に返り値相当の値(値域)を、
callback を呼び出した関数内で渡すことで、関数を合成することになる、んだと思う。
だからふつうに書くと、インデントが深くなっていってしまう。
// 値域とか定義域って言葉の使い方が変ですが、ニュアンスを感じ取っていただけると幸いです。 ajax( 'hoge.html', function callback( response ) { // response が ajax() の値域、そして callback の定義域 var bigNum = response.content + 100000000; // bigNum が callback の値域、そして longjob の定義域 longjob( bigNum, function format ( result ) { // result がlongjob の値域、そして format の定義域 var message = result.toString(); // message が format の値域、そして mail の定義域 mail( message, function notify ( feedback ) { // feedback が mail の値域、そして nofity の定義域 alert( feedback ); }); }); });
なので非同期処理の関数たちを合成するには、以下のようにしていけばいいわけですね。
(ここで、関数名() は、関数呼び出しじゃなくて関数の定義を表すとします。f(x) とか g(x) 的な。)
ajax( x ) ↓ 関数合成 ajax( x, callback( y ) ) ↓ 関数合成 ajax( x, callback( y, longjob( z ) ) ) ↓ 関数合成 ajax( x, callback( y, longjob( z, format( a ) ) ) ) ↓ 関数合成 ajax( x, callback( y, longjob( z, format( a, mail( b ) ) ) ) ) ↓ 関数合成 ajax( x, callback( y, longjob( z, format( a, mail( b, notify( c ) ) ) ) ) ) (これが最終的に合成された関数)
これに相当することをやってるのが brook だと concat メソッド で、
proto.concat = function(after){ var before = this; var next = function(n,val){ before.subscribe(after.ready(n),val); // ここ }; return new Promise(next); }; proto.ready = function(n){ var promise = this; return function(val){ promise.subscribe(n,val); }; };
ですね(多分)。
「関数合成」でぐぐると haskell とか scala の記事が出てきたから、関数型言語だとふつーに関数合成の概念が出てくるのかな。
すごいH本積んだままになってるからそろそろ読めってことか・・・。