缩进操作员

Operator on do indentation

hindent 将我的代码更改为:

do download i inputFile
   onException
     (callProcess (List.head args) (List.tail args))
     (removeFileIfExists name)
   `finally` removeFileIfExists inputFile

我无法确定 finally 是否适用于 do 块的其余部分,或者仅适用于 onException 开始的状态。根据this

If you see something unexpected in a list, like where, insert a closing brace before instead of a semicolon.

我不确定这条规则是否适用于此。

`finally` 是否适用于 do 的其余部分,还是仅适用于最后一条语句,为什么?

以下内容实际上并不是针对您发布的代码,而是针对您似乎要问的问题。它改为寻址以下代码:

do download i inputFile
   onException
     (callProcess (List.head args) (List.tail args))
     (removeFileIfExists name)
   ░`finally` removeFileIfExists inputFile

处理实际发布的代码,`finally` 的缩进不超过 downloadonException。我想说这是一种糟糕的风格:写的时候,一定要缩进 less,即

  do download i inputFile
     onException
       (callProcess (List.head args) (List.tail args))
       (removeFileIfExists name)
 `finally` removeFileIfExists inputFile

正如 Willem Van Onsem 评论的那样,函数应用程序 总是 优先于中缀应用程序。对于像 +>>= 这样的“真正的中缀运算符”和 backtick-infixes 都是如此......如果事实上就优先级而言,这些类型的运算符之间没有明显的区别关注:对于所有中缀,优先级由固定性声明确定,例如

<a href="http://hackage.haskell.org/package/base-4.10.1.0/docs/src/GHC.Num.html#%2B" rel="nofollow noreferrer">infixl 6 +</a>
<a href="http://hackage.haskell.org/package/base-4.10.1.0/docs/src/Data.Foldable.html#elem" rel="nofollow noreferrer">infix 4 `elem`</a>

fixity是0..9范围内的数字,fixity可以是left-right-或non-associative。如果没有声明固定性(与大多数命名函数一样,而对于 symbol-infixes 强烈建议提供固定性声明),则使用默认值 infixl 9,即基本上是您可以手动分配的最高合法固定性。

OTOH,函数应用程序是 always infixl 10,即它比任何中缀都更紧密,无论它是如何声明的。因此,您的示例被解析为

do (download i inputFile)
   (  (onException
        (callProcess (List.head args) (List.tail args))
        (removeFileIfExists name))
    `finally`
      (removeFileIfExists inputFile) )

我们可以用GHCi找出来:写

Prelude> let f = (>>)
Prelude> :{
Prelude| do print 5
Prelude|    print 4
Prelude|    `f` print 3
Prelude|    print 2
Prelude| :}

导致以下类型错误(不是解析错误!)

<interactive>:12:8: error:
    • Couldn't match expected type ‘(() -> IO ()) -> Integer -> IO b’
                  with actual type ‘IO ()’
    • The function ‘print’ is applied to three arguments,
      but its type ‘Integer -> IO ()’ has only one
      In the second argument of ‘f’, namely ‘print 3 print 2’
      In the expression:
        do { print 5;
             print 4 }
        `f` print 3 print 2

查看列表行,我们发现 GHCi 是如何解析代码的,该代码使用显式 braces-and-semicolons.

打印

在那里,我们看到 `f` 部分 关闭了 do 块!这使得整个 do 块成为 f 的第一个参数。此外,接下来的几行不再位于块中,现在形成单个表达式 print 4 print 2,用作 f 的第二个参数。这会触发类型错误,因为它使用三个参数调用 print

确实,由于 OP 提到的规则,大括号 } 被插入到 `f` 之前:当某些内容未在块中解析时,我们添加 } 并继续。

总结一下,如果`f`缩进more,则块被解析为

do print 5
   print 4 `f` print 3
   print 2

如果 `f` 缩进 前一行,或 less,该块被解析为

(do { print 5
    ; print 4 }) `f` print 3 print 2

我建议避免像前一行一样缩进 `f`:最好少缩进,这样即使对于人类来说解析也变得显而易见 reader.