Haskell do表示法边缘大小写无法进行类型检查



我正在努力理解do表示法的规则。

以下是一些进行类型检查的代码:

fff :: Maybe Int
fff = do
    _ <- pure $ Just 100
    (+10)
    <$> Just 50

基本上是CCD_ 2。我认为上面的内容可以而不是类型检查,因为肯定每一行都应该在Maybe的上下文中,而(+10)不是。

为什么要进行上述类型检查这里有一个更简单的例子:

fff :: Int -> Maybe Int
fff i = do
    (+10)
    <$> Just i

为什么上面的语法被认为是有效的?这不会"降低"到:

fff i = ((+10) >>= (i -> fmap (Just i))) i

这确实在ghci中给出了一个类型检查错误。


下面是一个在上面类似的缩进后执行而不是类型检查的示例:

x :: Maybe Int
x = do
  _ <- Just 1
  undefined
  <$> Just 5

(感谢FP slack聊天中的@cvlad提供上述示例(

这是一个奇怪的交互。

我开始将测试用例简化为这个,它运行得很好。

> x = do succ ; <$> Just 1
> x
Just 2

相比之下,这并不能解析:

> y = do { succ ; <$> Just 1 }      
error: parse error

然而,这解析为:

> z = do { succ } <$> Just 1      
> z
Just 2

以下是我的想法。由于令牌<$>永远无法启动表达式,所以解析暂时失败。do解析器规则本质上是一个最大咀嚼规则:失败时,添加一个隐式},然后重试。

因此,上面的x被解析为z。由于succ是一个一元值(OP问题中的(+10)行(,它可以出现在do内部。这将使类型检查成功。

引用Haskell报告2.7

只要语法类别包含布局列表结束;也就是说,如果一个非法的词位是在右大括号合法的情况下遇到插入支架。

fff :: Int -> Maybe Int
fff i = do
    (+10)
    <$> Just i

为什么上述语法被认为是有效的?

因为它被解析为

fff i = do {        -- do { A } is just
    (+10) }         --      A
    <$> Just i

相当于

fff i =
    (+10) 
    <$> Just i

因为<$> Just i本身是一个无效的表达式(因此fff i = ((+10) >>= (i -> fmap (Just i))) i是不正确的翻译(,并且根据@chi的回答中引用的规则来界定do块的范围。

事实上,它的类型被推断为

fff :: Num b => b -> Maybe b

如果在最后一行的<$>之前添加一个空格,则第二个示例有效。没有空间,它再次被解析为

inputTest :: FormInput -> IO (Either [String] (Int, Int))
inputTest fi = do {
    allErrors' <- undefined :: IO [String]
    undefined }
    <$> ((liftM2 ) (,) <$> undefined <*> undefined) fi

,因为<$> ...本身是无效的表达式

inputTest2 :: String -> IO (Either [String] (Int, Int))
inputTest2 fi = do {
    allErrors2 <- undefined :: IO [String] ;
    undefined  }
    <$> ((liftM2 ) (,) <$> undefined <*> undefined) fi

我在TIO上收到了完全相同的错误消息

(必须使用String而不是您的类型(。

自从第一个undefined :: IO [String]以来,整个do块都有一些fff = (+10) <$> Just 500类型,我们不能在任何东西上形成

总是添加所有显式分隔符(除了练习良好的缩进样式(,以避免这种奇怪的语法脆性。


您的新示例是

x :: Maybe Int
x = do          -- {     this is
  _ <- Just 1   -- ;       how it is
  undefined     -- }         parsed
  <$> Just 5

代码变了,但答案是一样的。在<$>之前的doMaybe t(因为Just 1(,并且我们不能形成

同样,再缩进最后一行,它就会编译,因为undefined <$> Just 5现在将被解析为一个表达式。

最新更新