Требуется ли для выражения case в выражении let скобки и точки с запятой? - PullRequest
6 голосов
/ 21 марта 2019

Я запутался в правилах синтаксического анализа 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 я не получу здесь?

1 Ответ

11 голосов
/ 21 марта 2019

Из отчета Haskell 2010 :

Неформально указано, что фигурные скобки и точки с запятой вставляются следующим образом.Правило макета (или «офсайд») вступает в силу всякий раз, когда после ключевого слова where, let, do или of пропускается открывающая фигурная скобка.Когда это происходит, отступ следующего лексемы (независимо от того, находится ли он на новой строке) запоминается и вставляется пропущенная открывающая скобка (пробел, предшествующий лексеме, может включать комментарии).Для каждой последующей строки, если она содержит только пробел или больше отступов, предыдущий элемент продолжается (ничего не вставляется);если отступ в том же количестве, начинается новый элемент (точка с запятой вставляется);и если отступ меньше, то список макетов заканчивается (вставляется закрывающая скобка) ...

... Кроме того, эти правила разрешают:

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

ЭтоНа самом деле пример показывает, как вы должны относиться к отступу в Haskell, в основном это не ключевые слова , которые определяют, что с отступом что-то, скорее это первый идентификатор (или другая лексема) после них, так что в случаеиз

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

Это хорошо, поскольку 2-я и 3-я строки имеют отступ больше, чем 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

Оба из которыхich будет проанализирован, как вы ожидаете.

...