The Mother of all 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]
- 但为什么要编译?
答案 0 :(得分:1)
首先,让我们看看如何定义Cont
:
data Cont r a = Cont {runCont :: (a -> r) -> r}
如果放大此声明,您将看到Cont
包含一个函数,给定函数(a->r)
生成r
。换句话说,它隐藏了#34;类型a
(或r
)&#34;内部&#34; runCont
。因此Cont (\fred -> "escape")
表达式中fred
(do
是一个函数,并被忽略)的存在告诉我们r
是String
,{{1修复整个表达式以返回类型为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
可以&#34;隐藏&#34;类型runCont
的值直接,不一定是类型r
的值 - 然后是&#34;继续&#34; (a
)中的fred
未被调用,您将观察&#34; escape&#34;。
因此,Cont (\fred -> "escape")
表示a <- return 1
,显然,a :: Integer
并不代表b <- Cont (\_ -> "escape")
- 相反,b :: String
可以是任何类型 - function {{传递给b
的1}}被忽略,因此返回fred
的任何函数都可以工作 - 但Cont
的类型由表达式的其余部分修复。 String
仅表示b
- 由于return $ a + b
为Cont String Integer
,因此a
也被定义为Integer
。
另外,请注意b
中的Integer
定义为show
表达式的最后一行的延续:它适用于行runCont ex3 show
,所以你打算传递do
类型的函数,因为return $ a+b
是Integer -> r
,你打算传递a+b
类型的函数,因为Integer
是由表达式a -> String
修复。
然后整个表达式等同于以下内容:
r