Continuation Monad (1)

t-y-schemeの継続についての章であるChapter 13をHaskellで書いてみる。

まず13.1章。

13.1 call-with-current-continuation

Example 1
(+ 1 (call/cc
       (lambda (k)
         (+ 2 (k 3)))))
=>  4

Continuationモナドを利用する。(+)や整数リテラルがtype classのメソッド(?)であることを利用してliftMすることにする。

instance Show a => Show (Cont r a) where
instance Eq a => Eq (Cont r a) where
instance Num a => Num (Cont r a) where
  a + b = liftM2 (+) a b
  fromInteger = return . fromInteger

そうすると:

f00 :: Cont r Int
f00 = 1 + callCC (\k -> 2 + k 3)
*Main> runCont f00 id
4

元のコードそっくりである*1

Example 2
(define r #f)

(+ 1 (call/cc
       (lambda (k)
         (set! r k)
         (+ 2 (k 3)))))
=>  4

(+ 3 (r 5))
=>  6

今度は継続を保存しなくてはいけない。Stateモナドでやってみる。型が自己再帰するので、ラップするための型が必要になる。

newtype ContTState r a b = ContTState {
  unContTState :: ContT r (State (a -> ContTState r a b)) b }

本体はこうなる。

f03 :: ContT r (State (Int -> ContTState r Int Int)) Int
f03 = 1 + callCC (\k -> do put (ContTState . k); 2 + k 3)

f04 = flip evalState undefined $ 
    do  x <- runContT f03 return
        r <- get
        y <- runContT (3 + unContTState (r 5)) return
        return (x, y)
instance Show a => Show (ContT r m a) where
instance Eq a => Eq (ContT r m a) where
instance (Monad m, Num a) => Num (ContT r m a) where
  a + b = liftM2 (+) a b
  fromInteger = return . fromInteger
*Main> f04
(4,6)

でもSTモナドなら余計な型がいらず、簡単。

f05 :: STRef s (Int -> ContT r (ST s) Int) -> ContT r (ST s) Int
f05 r = 1 + callCC (\k -> do lift (writeSTRef r k); 2 + k 3)

f06 = runST (do r <- newSTRef undefined
                x <- runContT (f05 r) return
                k <- readSTRef r
                y <- runContT (3 + k 5) return
                return (x, y))
*Main> f06
(4,6)

*1:Schemeでは値を読むときにも副作用が生じ得るので、何か演算子を利用して関数適用も全てliftMしたほうが意味的に近くなると思う。しかし見た目が良くないので、以後も関数適用はできる限りHaskellの記法そのままにすることにする