memo: monad in javascript


(function(){

//unit: a -> M[b]
var unit = function(value){
    return {
        value : function(){return value}
    };
};
//bind: M[a] -> (a->M[b]) -> M[b]
var bind = function(m,proc){
    return proc( m.value() );
};

// func a->M[b]
var x2 = function(x){return unit(x*x)};
var inc = function(x){return unit(x+1)};

//return a >>= f ≡ f a
print( bind( unit(10) , x2 ).value(), x2(10).value());
//m >>= return ≡ m
print( bind( unit(10) , unit ).value(),unit(10).value());

//(m >>= f) >>= g ≡ m >>= (\x -> f x >>= g)
print(
    bind( bind( unit(10) , x2 ) ,inc ).value(),
    bind( unit(10) , function(x){ return bind( x2(x) ,inc)}).value()
);

})();

評価とか型とかないからあれだけど、
一番単純な形だとこんな雰囲気になるはず。

これだとなんの役にも立たないので、構造を覚えていて
valueが呼ばれるまで評価しないmonadを作ってみる。

(function(){

//unit: a -> M[b]
var unit = function(value){
    var ret = (value.call)  ? value : 
              (value.value) ? value.value : function(){return value}
    return {
        value : ret
    };
};
//bind: M[a] -> (a->M[b]) -> M[b]
var bind = function(m,proc){
    return unit(function(){ return proc( m.value() ).value() });
};
// bindが逐次実行じゃなくて評価前の状態を保存する
// ように作る。

// func a->M[b]
var x2 = function(x){return unit(x*x)};
var inc = function(x){return unit(x+1)};

print( unit(10).value());
print( unit(unit(10)).value())
print( bind(unit(10),x2).value());
print( bind( bind(unit(10),x2),inc).value());

//return a >>= f 竕。 f a
print( bind( unit(10) , x2 ).value(), x2(10).value());
//m >>= return 竕。 m
print( bind( unit(10) , unit ).value(),unit(10).value());

//(m >>= f) >>= g 竕。 m >>= (\x -> f x >>= g)
print(
    bind( bind( unit(10) , x2 ) ,inc ).value(),
    bind( unit(10) , function(x){ return bind( x2(x) ,inc)}).value()
);


var process = [inc,inc,x2,x2,inc];
var m = unit(1);
process.forEach(function(e){
    m = bind( m , e );
});
print("before evaluate");
print(m.value());
print("afater evaluate")

})();

Array.prototype.flatMapを実装するとArrayもmonadに。

Array.prototype.flatMap = function(f){
    var ret = [];
    this.forEach(function(e){
        ret = ret.concat( f(e) );
    });
    return ret;
};

//unit: a -> M[b]
var unit = function(value){
    return [value];
};
//bind: M[a] -> (a->M[b]) -> M[b]
var bind = function(m,proc){
    return m.flatMap(proc);
};

// func a->M[b]
var x2 = function(e){return unit(e*e)};
var inc = function(e){return unit(e+1)};

//return a >>= f == f a
print( bind( unit(10) , x2 ), x2(10));
print( [10].flatMap(x2),x2(10));
//m >>= return == m
print( bind( unit(10) , unit ),unit(10));
print( [10].flatMap(function(e){return [e]}), [10]);


//(m >>= f) >>= g == m >>= (\x -> f x >>= g)
print(
    bind( bind( unit(10,1) , x2 ) ,inc ),
    bind( unit(10,1) , function(x){ return bind( x2(x) ,inc)})
);

print(
    [10].flatMap(x2).flatMap(inc),
    [10].flatMap(function(x){ return x2(x).flatMap(inc)})
);