为什么Haskell假定返回的monad类型与作为参数传递的类型相同?

gaa*_*kam 5 monads haskell types function

为什么要编译此代码?

--sequence_mine :: Monad m => [m a] -> m [a]
sequence_mine [] = return []
sequence_mine (elt:l) = do
  e <- elt
  sl <- sequence l
  return (e:sl)
Run Code Online (Sandbox Code Playgroud)

注意我在这里特意注释掉了类型声明。但是即使没有类型声明,代码仍然可以编译并且可以按预期工作-这让我感到惊讶。

据我了解,在这方面应该产生歧义:

return (e:sl)
Run Code Online (Sandbox Code Playgroud)

原因是Haskell不应该知道我们要返回哪种类型的monad。为什么它必须与我们接受的类型相同?

要澄清更多。据我了解,如果我不明确地将类型声明与我注释过的类型声明类似,Haskell应该推断该函数具有如下类型:

sequence_mine :: (Monad m1, Monad m2) => [m1 a] -> m2 [a]
Run Code Online (Sandbox Code Playgroud)

除非我明确地统一起来m1m2通过将它们两者都调用m,否则Haskell没有理由相信它们都引用相同的类型!我想。

但是事实并非如此。我在这里想念什么?

Rob*_*ond 8

好吧,让我们看一下该do块可以解决的问题:

sequence_mine (elt:l) = elt >>= \e -> (sequence l) >>= \sl -> return (e:sl)
Run Code Online (Sandbox Code Playgroud)

回想一下,“ bind”运算符>>=具有类型签名(Monad m) => m a -> (a -> m b) -> m b。请注意,m此处的monad 尽管是任意的,但对于参数和结果类型两者都必须相同。

因此,如果elt具有type m a,则很容易看到return (e:sl)-是整个表达式的输出类型- m [a]对于同一monad 必须具有type m

换句话说,每个do块仅在固定monad的上下文中起作用。