let 语句中的 case 语句需要什么缩进?

What indentation is required for a case statement within a let statement?

在 haskell 工作,发现奇怪的行为,将其精简到最基本的状态

这有效

a :: Bool
a = case True of
    True -> True
    False -> False

但是当我尝试

b :: IO Bool
b = do
    let b' = case True of
        True -> True
        False -> False
    return b'

我明白了

ghci>:l test.hs
[1 of 1] Compiling Main             ( test.hs, interpreted )

test.hs:16:14: parse error on input ‘->’
Failed, modules loaded: none.

所以我试试

c :: IO Bool
c = do
    let c' = case True of
            True -> True
            False -> False
    return c'

这行得通。

什么?为什么?为什么在这种情况下需要额外的缩进?我找不到任何关于此的内容,可能是因为这些关键字在日常语言中是如此简短和常见。是否有一些规范可以解释这种行为?

我没有规范中的确切措辞,但是这个 Wikibook page 非常清楚地解释了这个问题。

这样做的原因很简单:支持通过单个 let-group 绑定多个变量,例如:

c = do
    let c' = …
        d  = …
        e  = …
    return c'

您的 True -> …False -> … 被错误地解释为要绑定的附加变量。

基本的缩进规则其实很简单:

  • 在开始一个块的关键字之后 (where,let,do,case .. of) 记下下一个单词开始的列(可能是在下一行)
  • 行缩进与块中的新条目完全相同
  • 缩进的行多于继续上一个条目的行
  • 缩进小于该行的那一行恰好在该行之前结束该块
  • 在嵌套块中,首先将规则应用于最外层的块

棘手的例子:

1 + case x of
      A -> 45  -- note where "A" starts
      B -> 10  -- same indentation: another case branch
       + 2     -- more indented, so it's "10+2"
     + 10      -- less indented, so it's "1+(case ...)+10"

在你的情况下,

let b' = case True of
    True -> True
    False -> False

我们有两个嵌套块,一个用于 let,一个用于 case..oflet 块使用 b' 的列。 case..of 块尝试重用同一列,但我们需要首先将规则应用于最外层的块。所以 True -> ... 行实际上是 let 块的新条目。这会触发解析错误。