Какой отступ требуется для оператора case в операторе let?

Работая в хаскеле, нашел странное поведение, раздев его до голой кости

Это работает

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'

И это работает.

Какие? Зачем? Зачем мне нужен дополнительный отступ в этом случае? Я ничего не могу найти по этому поводу, вероятно, потому что эти ключевые слова очень короткие и часто встречаются в повседневном языке. Есть какая-то спецификация, которая объясняет это поведение?

2 ответа

Решение

У меня нет точной формулировки из спецификации, но эта страница Wikibook объясняет проблему довольно четко.

Причина, по которой это работает так, проста: поддерживать связывание нескольких переменных с помощью одной группы let, например:

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..of, let блоки использует столбец b', case..of Блок пытается повторно использовать тот же столбец, но сначала нам нужно применить правила к самому внешнему блоку. Итак True -> ... линия на самом деле новая запись let блок. Это вызывает ошибку разбора.

Другие вопросы по тегам