ここから先は、独自実装です。何か問題があればコメントお願いします。
まとめ: ret,skip を実装するだけで、パイプラインの中で分岐できます。 お手軽で うれしい!
もっとお手軽にしてみる
前の記事で、pipe、ret、bind、callCCの合わせ技で分岐ができるっていうのはわかりました。でも、もっとお手軽なのがいいな...というわけで、もっと簡単にできないか考えてみました。
const ret = x =>
k => k(x)
const skip = x => _ =>
k => k(x)
値を継続に渡す ret、継続を一個だけ無視してその次の継続に渡す skip を定義してみました。これだけです。pipe/bind/callCCは無しです。
これを使ってフィボナッチ数列を求める関数を書いてみました。
// 使用例:
const fibCPS = n =>
( n < 2 ? skip(1) : fibCPS(n - 2) )
( a => fibCPS(n - 1)
( b => ret(a + b) )
)
fibCPS(5)(x=>x) // 8
callCCほど高機能ではないですが、これですむことはかなり多いんじゃないかと思います。
また、skip 単体では継続をひとつ飛ばすことしかできませんが、そこに複数の継続を入れておけば、callCC相当のことができそうです。
前の記事のcallCCの例です。
const g = x =>
pipe( callCC( exit => pipe( x === 0 ? exit(x) : ret(x) )
( bind( x => ret(x + 1)) )
( bind( x => ret(x + 1)) )
()
)
)
( bind( x => ret(x + 1)) )
( evalCont )
()
g(0) // 1
g(1) // 4
同じことを skip でやるならこうです。
const g = x =>
( x === 0 ? skip(x) : ret(x) )
( x => ret(x + 1)
( x => ret(x + 1) )
)
( x => ret(x + 1) )
( x=>x )
g(0) // 1
g(1) // 4
exitが callCC内の継続を無視して、外部のcallCCの継続に渡す、っていう性質と、
skipが 次の継続を無視して、その次の継続に渡す、っていう性質が対応しています。
ちなみに、単にかっこでくくってやるだけで:
const g = x =>
( ( x === 0 ? skip(x) : ret(x) )
( x => ret(x + 1)
( x => ret(x + 1) )
)
)
( x => ret(x + 1) )
( x=>x )
callCC の構造っぽく見せることもできます。