为什么这段代码会编译?
--sequence_mine :: Monad m => [m a] -> m [a]
sequence_mine [] = return []
sequence_mine (elt:l) = do
e <- elt
sl <- sequence l
return (e:sl)
注意,我在这里特意注释掉了类型声明。但是代码仍然可以编译,并且即使没有类型声明也可以按预期工作-这让我感到惊讶。
据我了解,此行应该会产生歧义:
return (e:sl)
原因是Haskell不应该知道我们要返回哪种类型的monad。为什么它必须与我们接受的类型相同?
进一步说明。据我了解,如果我不明确地将类型声明与我注释过的类型声明类似,Haskell应该推断该函数具有如下类型:
sequence_mine :: (Monad m1, Monad m2) => [m1 a] -> m2 [a]
除非我通过将m1
和m2
分别称为m
来明确地统一它们,否则Haskell没有理由相信它们都引用相同的类型!我想。
但事实并非如此。我在这里想念什么?
答案 0 :(得分:8)
好吧,让我们看一下do
阻止的目标:
sequence_mine (elt:l) = elt >>= \e -> (sequence l) >>= \sl -> return (e:sl)
回想一下,“绑定”运算符>>=
具有类型签名(Monad m) => m a -> (a -> m b) -> m b
。请注意,此处的monad m
尽管是任意的,但对于参数和结果类型两者都必须相同。
因此,如果elt
的类型为m a
,很容易看出return (e:sl)
(是整个表达式的输出类型)必须具有类型m [a]
,对于相同的单子m
。
换句话说,每个do
块仅在固定monad的上下文中起作用。