人生ずっと勉強

人生ずっと勉強ですね。 https://twitter.com/KiyotakaGoto

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本積んだままになってるからそろそろ読めってことか・・・。