Haskell解析错误缩进的错误

xiv*_*r77 4 haskell

任何人都可以解释为什么这是一个语法错误?

f =
  f'
  where f' = do
    if True then
      return ()
    else
      return ()

main = f
Run Code Online (Sandbox Code Playgroud)

如果我给if块更多缩进,那么它会以某种方式编译好.

f =
  f'
  where f' = do
        if True then
          return ()
        else
          return ()

main = f
Run Code Online (Sandbox Code Playgroud)

或者我可以分开where,我通常会这样做.

f =
  f'
  where
    f' = do
      if True then
        return ()
      else
        return ()

main = f
Run Code Online (Sandbox Code Playgroud)

我开始赏金以获得下面两个问题的好解释.(是的,我读了Haskell报告.因为不理解10.3布局而感到羞耻)

  1. 为什么第一个例子是错误的?
  2. 为什么第二个例子不是错误?

Sam*_*den 6

您违反的规则在第10.3节的注释1中解释.我来引述一下:

注意1. 嵌套上下文必须比封闭上下文(n> m)进一步缩进.如果不是,则L失败,编译器应指示布局错误.一个例子是:[例子,见下文] 这里,p缩进的定义小于封闭上下文的缩进,在这种情况下通过定义来设置h.

示例如下:

f x = let  
         h y = let  
  p z = z  
               in p  
      in h
Run Code Online (Sandbox Code Playgroud)

在您的情况下,if-statement 的上下文是f'定义.(正如在示例中,定义的上下文p是定义h.)由于if-statement是嵌套上下文,因此它需要比封闭上下文进一步缩进(即if需要进一步缩进f').这就是您在第一个代码段中出现错误的原因.

在你的第三个例子p中进一步缩进f',这是符合规则的.


编辑:

奇怪的是,你的第二个例子没有给出错误,因为p它没有缩进f',但是有相同的缩进,而且几乎看起来这不是故意的(即一个bug).我做了一些研究,发现do声明实际上有所不同.以下是错误:

f =
  f'
  where f' = -- no do
        if True then
          return ()
        else
          return ()
Run Code Online (Sandbox Code Playgroud)

where/ let和之间的相互作用do似乎引起并发症.检查以下示例:

1)编译没有问题:

f = let y = do
        Just 1
    in y
Run Code Online (Sandbox Code Playgroud)

2)这会导致错误:

f = let y =
        Just 1
    in y
Run Code Online (Sandbox Code Playgroud)

3)编译没有问题:

f = do
 Just 1
Run Code Online (Sandbox Code Playgroud)

4)这会导致错误:

f = do
Just 1
Run Code Online (Sandbox Code Playgroud)

这里很奇怪的是(4)中的错误与(1)的成功解析不一致,并且(1)的成功解析似乎与前面陈述的规则不一致(嵌套上下文需要进一步缩进).

编辑2:

好吧,似乎我已经深究了它!(我继续提交了一份错误报告,但由于EZ Yang的解释,该报告被标记为无效.)

这是交易:在某些情况下,使用do-notation时不需要不断增加缩进.因此,实现了语言扩展NondecreasingIndentation,默认情况下处于启用状态.有关更多信息,请参见此处

可以关闭此功能,以便在增加if-then-else-part 的缩进之前,不会编译以下版本的代码:

{-# LANGUAGE NoNondecreasingIndentation #-}

f =
  f'
  where f' = do
        if True then
          return ()
        else
          return ()
Run Code Online (Sandbox Code Playgroud)

NondecreasingIndentation功能仅在已缩进的上下文处于活动状态.这就是为什么我的上一个例子(4),即使启用了扩展,也没有编译.

TL; DR

缺口的金科玉律普遍认为,有一个例外.缩进块do不需要比其封闭上下文进一步缩进.要do以这种方式阻止GHC以不同方式处理-blocks,请启用语言扩展NoNondecreasingIndentation.