限定継続その2
まずは元論文にもある リストの appendを限定継続で書く例。この時点でついて行けてなかった。
実はOlegさんのソースにもあるのだけど自分で書き直してみる。
-- -fno-implicit-prelude を指定すること (GHC 6.6) import ShiftResetGenuine import Prelude hiding ((>>=),(>>),return) m >>= f = m `bind` f m >> n = m >>= (\_ -> n) return x = ret x appendCont (x:xs) = appendCont xs >>= \xs' -> return (x:xs') -- doを使わない理由は後述 appendCont [] = shift (\k -> return k) append123 ys = do k <- reset $ appendCont [1,2,3] k ys -- *Main> run (append123 [3,4,5]) -- [1,2,3,3,4,5]
shiftは大域脱出のための演算子のようなもの。shift (\k -> e) でresetまで大域脱出が起こり結果がeになる。 ここで k には reset-shift間で切り出した限定継続が入る。 この限定継続 kというのは、引数を食わせると、その引数がちょうどshiftの位置に登場してresetまでの残りの計算が実行されて戻るという関数。 つまり reset ( .... (shift (\k -> e)) ...) ==> let k x = (.... x ....) in e という感じなのだけどこれは他の解説でもよく目にする。
上の例では、モナドがあってややこしいけど、reset-shiftで切り出した限定継続を、そのまま返している (\k->return k)。
reset 内の項 appendCont [1,2,3] は do xs <- shift (ほげほげ); return (1:2:3:xs) のように簡約され(るというのはたぶん嘘で単にこういう項と等価になる;めんどいので以下略)、ここで shiftで切り出された限定継続kは (\xs -> return (1:2:3:xs)) という感じになる。 (\k->return k) でこの限定継続がそのまま reset の戻り値になる。
なんとなくわかってきた気がするけど、型のことを考えると、より一層混乱するのは言うまでもない。
do-notation bug?
GHC6.6で、-fno-implicit-prelude を加えて以下をコンパイル。
import ShiftResetGenuine import Prelude hiding ((>>=),(>>),return) m >>= f = m `bind` f m >> n = m >>= (\_ -> n) return x = ret x appendCont (x:xs) = do xs' <- appendCont xs return (x:xs')
これの型が appendCont_ :: (Monadish m) => [t] -> m a a [t] となってしまう。 do記法が定義どおり展開されているならば (Monadish m) => [t] -> m a g [t] となるはず。 式全体の型がreturn (x:xs')の型に縛られている? 普通のMonadならそれでも大丈夫なんだけど…。