【问题标题】:Does a case expression in a let expression require braces and semicolons?let 表达式中的 case 表达式是否需要大括号和分号?
【发布时间】:2019-08-12 04:27:14
【问题描述】:

我对 Haskell 的解析规则感到困惑。

这很好用:

n = 5
m = 6
b = case (n, m) of
  (5, 6) -> True
  _ -> False

main = print b

让我们稍微复杂一点,让我们在混合物中添加一个let:

b =
  let res = case (n, m) of
    (5, 6) -> True
    _ -> False
  in not res

(注意,为简洁起见,从现在开始,我将省略n、m 和main 的定义;它们继续进行,我只是更改b)

糟糕,这里有问题:

wtf.hs:5:5: error: parse error on input ‘(’
Failed, modules loaded: none.

我不知道,也许这是我没有得到的某种奇怪的缩进规则。好吧,让我们把明确的括号:

b =
  let res = case (n, m) of {
    (5, 6) -> True
    _ -> False }
  in not res

还是不行?!

wtf.hs:6:7: error: parse error on input ‘->’
Failed, modules loaded: none.

我完全糊涂了。我不知道该怎么办。为什么这行不通??让我们在这里添加一个明确的分号,尽管这确实是一个盲目的镜头,即使我不明白为什么在这里需要它,因为毕竟,AFAIK,一个换行符(它存在于这里)应该使分号变得多余:

b =
  let res = case (n, m) of {
    (5, 6) -> True;
    _ -> False }
  in not res

这终于奏效了!

...不知道,可能问题在于let 和case 在同一行。作为我自己研究的最后一次尝试,让我们试试这个:

b =
  let res =
    case (n, m) of
      (5, 6) -> True
      _ -> False
  in not res

但是由于对我来说很神秘的原因,这不起作用:

wtf.hs:5:5: error:
    parse error (possibly incorrect indentation or mismatched brackets)
Failed, modules loaded: none.

说真的,我在这里很困惑。为什么这里需要显式括号和分号? (它们是吗?可以将代码格式化为不需要它们的方式吗?)

我没有在这里了解 Haskell 的哪个晦涩的解​​析规则?

【问题讨论】:

  • 我觉得this answer可以帮你理清Haskell的缩进规则。 (相关问题也和你的很相似。)
  • “换行符(此处存在)应该使分号多余”仅当您没有明确的大括号时!

标签: haskell switch-statement let


【解决方案1】:

From the Haskell 2010 Report:

通俗地说,大括号和分号的插入方式如下。只要在关键字 where、let、do 或 of 之后省略左大括号,布局(或“越位”)规则就会生效。发生这种情况时,会记住下一个词素的缩进(无论是否在新行上)并插入省略的左大括号(词素前面的空格可能包括 cmets)。对于随后的每一行,如果它只包含空格或缩进更多,则继续上一项(不插入任何内容);如果缩进量相同,则开始一个新项目(插入分号);如果缩进较少,则布局列表结束(插入右括号)...

...此外,这些规则允许:

f x = let a = 1; b = 2  
          g y = exp2  
       in exp1 

这个例子实际上展示了你应该如何处理 Haskell 中的缩进,基本上不是 关键字 决定了缩进的程度,而是它后面的第一个标识符(或其他词位) ,所以在

的情况下
b = case (n, m) of
  (5, 6) -> True
  _ -> False

这很好,因为第二行和第三行比第一行中的b 缩进更多,另一方面,以下

b =
  let res =
    case (n, m) of
      (5, 6) -> True
      _ -> False
  in not res

本质上被解析为

b =
  let { res =
    } case (n, m) of
     { (5, 6) -> True
     ; _ -> False
  } in not res  

这是因为case 的缩进不超过res,所以它不是其定义的一部分。
这就是编译器抱怨解析错误的原因(它期望在= 之后有一个词位但什么也得不到,它也不期望那里有case,因为它不符合let ... in ... 语法)。

你应该写

b =
  let res =
        case (n, m) of
          (5, 6) -> True
          _ -> False
  in not res

或

b =
  let res = case (n, m) of
        (5, 6) -> True
        _ -> False
  in not res

两者都将按照您的预期进行解析。

【讨论】:

  • 嗯...我很困惑。您说应该可以工作的最后一个代码 sn-p 与我的第一个不工作代码 sn-p 基本相同?唯一的区别是您将(5, 6) 缩进了几个空格?
  • @gaazkam 不一样,注意第3行和第4行缩进更多
  • @gaazkam (5,6) 应该比res 缩进更多而不是let,相同let ... in ... 中的后续定义应该缩进与res 一样多/跨度>
  • 好的,终于搞定了。谢谢。
  • @gaazkam:我更喜欢的另一种方法是在let 之后简单地放置一个换行符+缩进,就像使用do 块一样,那么缩进是统一的。
猜你喜欢
  • 2019-04-18
  • 1970-01-01
  • 2021-01-19
  • 2013-05-02
  • 1970-01-01
  • 2013-01-20
  • 1970-01-01
  • 2023-04-02
  • 1970-01-01
相关资源
最近更新 更多