Continuation Monad - Int and String



所有Monads之母的文章提供了以下代码:

ex3 = do
  a <- return 1
  b <- ContT (fred -> "escape")
  return $ a+b

然后我可以执行它:

ghci> runContT ex3 show
"escape"

但我不能跑:

ghci> runContT ex3 id
<interactive>:51:14:
    Couldn't match type ‘[Char]’ with ‘Integer’
    Expected type: Integer -> [Char]
      Actual type: Integer -> Integer
    In the second argument of ‘runContT’, namely ‘id’
    In the expression: runContT ex3 id

return $ a+b如何进行类型检查?

runCont ex3 show是怎么回事?runCont ex3 id怎么样——看起来不能Int + [Char]——但它为什么要编译?

首先,让我们看看Cont是如何定义的:

data Cont r a = Cont {runCont :: (a -> r) -> r}

如果放大此声明,您将看到Cont包装了一个函数,给定一个函数(a->r)产生r .换句话说,它"隐藏"了类型a(或r)的值"内部"runCont。所以do表达式中Cont (fred -> "escape")fred是一个函数,被忽略)的存在告诉我们rString的,整个表达式的runCont是固定的返回一个String类型的值,并且只能接受a->String类型的函数对于某些a, 我们需要解决这个问题。

现在让我们看看return是什么样的:

instance Monad (Cont r) where
    return x = Cont ($ x)  -- given a function, pass x to it
    (Cont m) >>= f = -- f :: a -> Cont r b, which roughly means
                     -- f :: a -> (b->r) -> r
                     -- which would roughly be Cont (g -> m (flip f g))
                     -- notice we pass f to m: f is the stuff that needs
                     -- executing when m needs to; f is continuation of m
                     let h g x = let (Cont n) = f x -- unwrap (b->r)->r
                                 in n g
                     in Cont (m . h)
    -- or Cont (g -> m (($ g) . runCont . f)), if that's easier to read

请注意>>=的工作原理。如果m不使用传递给它的函数 - 记住,runCont可以直接"隐藏"类型r的值,不一定是类型a的值 - 那么"延续"(fred Cont (fred -> "escape"))不会被调用,你将观察到"escape"。

所以,a <- return 1的意思是a :: Integer,显然,b <- Cont (_ -> "escape")并不意味着b :: String - 相反,b可以是任何类型的 - 传递给Cont的函数fred被忽略,因此任何返回String的函数都将起作用 - 但b的类型由表达式的其余部分固定。 return $ a + b的意思是Cont String Integer - 因为aInteger的,所以b也是固定的Integer

另外,请注意,根据定义,runCont ex3 show 中的 show 注定是 do 表达式最后一行的延续:它用于行return $ a+b,所以你意味着传递类型 Integer -> r 的函数,因为a+bInteger的,而你打算传递类型 a -> String 的函数,因为r是由表达式 b <- Cont (_ -> "escape") 固定的。

然后,整个表达式等效于以下内容:

do
  a <- return 1
  b <- Cont (_ -> "escape")
  return $ (a+b)
==
  return 1 >>= (a -> (Cont (_ -> "escape") >>= (b -> return (a+b))))
== -- apply return rule
  Cont ($ 1) >>= (a -> (Cont (_ -> "escape") >>= 
                        (b -> Cont ($ (a+b)))))
== -- apply >>= rule
  Cont (g -> ($ 1) (($ g) . runCont . (a -> (Cont (_ -> "escape") >>= 
                                              (b -> Cont ($ (a+b)))))))
== -- apply >>= rule
  Cont (g -> ($ 1) (($ g) . runCont . (a -> (Cont (h -> 
        (_ -> "escape") (($ h) . runCont . (b -> Cont ($ (a+b)))))))))
== -- (_ -> x) y == x
  Cont (g -> ($ 1) (($ g) . runCont . (a -> (Cont (h -> "escape")))))
== -- marking unused variables with "_" for clarity
  Cont (g -> ($ 1) (($ g) . runCont . (_ -> (Cont (_ -> "escape")))))
== -- ($ y) (_ -> x) == x
  Cont (g -> ($ g) $ runCont (Cont (_ -> "escape")))
== -- runCont (Cont x) == x
  Cont (g -> ($ g) (_ -> "escape"))
== -- ($ y) (_ -> x) == x
  Cont (_ -> "escape")

最新更新