haskell indentation do-notation

haskell - ¿Qué sangrado se requiere para una declaración de caso dentro de una declaración let?



indentation do-notation (2)

Trabajando en Haskell, encontré un comportamiento extraño, lo despojé hasta los huesos

Esto funciona

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

Pero cuando lo intento

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

yo obtengo

ghci>:l test.hs [1 of 1] Compiling Main ( test.hs, interpreted ) test.hs:16:14: parse error on input ‘->’ Failed, modules loaded: none.

Así que lo intento

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

Y esto funciona.

¿Qué? ¿Por qué? ¿Por qué necesito una sangría adicional en este caso? No puedo encontrar nada sobre esto, probablemente porque estas palabras clave son muy cortas y comunes en el lenguaje cotidiano. ¿Hay alguna especificación que explique este comportamiento?


Las reglas básicas de sangría son en realidad bastante simples:

  • después de las palabras clave que comienzan un bloque ( where , let , do , case .. of ) anote la columna donde comienza la siguiente palabra (que podría estar en la línea siguiente)
  • líneas sangradas exactamente como que son nuevas entradas en el bloque
  • líneas sangradas más que eso continúan la entrada anterior
  • una línea sangrada menos que eso termina el bloque justo antes de esa línea
  • en bloques anidados, aplique las reglas al bloque más externo, primero

Ejemplo complicado:

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"

En tu caso,

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

tenemos dos bloques anidados, uno para let y otro para case..of . Los bloques let usan la columna de b'' . El case..of bloque intenta reutilizar la misma columna, pero primero debemos aplicar las reglas al bloque más externo. Entonces, la línea True -> ... es en realidad una nueva entrada del bloque let . Esto desencadena un error de análisis.


No tengo la redacción exacta de la especificación, pero esta página de Wikibook explica el problema con bastante claridad.

La razón por la que funciona así es simple: para admitir la vinculación de múltiples variables a través de un solo let-group, como:

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

Su True -> … y False -> … se interpretan erróneamente como variables adicionales a vincular.