为什么要编译这段代码?
--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没有理由相信它们指的是同一类型!我想是的。
然而,事实并非如此。这里我漏掉了什么?
发布于 2019-03-26 00:15:55
好了,让我们来看看do块的目的是什么:
sequence_mine (elt:l) = elt >>= \e -> (sequence l) >>= \sl -> return (e:sl)回想一下,“绑定”操作符>>=具有类型签名(Monad m) => m a -> (a -> m b) -> m b。请注意,这里的单体m虽然是任意的,但对于参数和结果类型必须是相同的。
因此,如果elt具有类型m a,则很容易看出return (e:sl) --即整个表达式的输出类型--必须具有类型m [a],用于相同的monad m。
换句话说,每个do块只在固定monad的上下文中工作。
https://stackoverflow.com/questions/55341953
复制相似问题