2010-02-08 16 views
19

este código:Haskell "donde" sangría: ¿por qué debe ser indentado pasado identificador?

import Data.Char (digitToInt) 

myInt :: String -> Int 
myInt [] = error "bad input: empty string" 
myInt (x:xs) 
    | x == '-' = -1 * myInt xs 
    | otherwise = foldl convert 0 (x:xs) 
    where convert acc x 
     | x `elem` ['0'..'9'] = 10 * acc + digitToInt x 
     | otherwise   = error ("bad input: not an int - " ++ [x]) 

falla:

Prelude> :l safeListFs.hs 
[1 of 1] Compiling Main    (safeListFs.hs, interpreted) 

safeListFs.hs:9:8: parse error (possibly incorrect indentation) 
Failed, modules loaded: none. 

Pero esta versión:

import Data.Char (digitToInt) 

myInt :: String -> Int 
myInt [] = error "bad input: empty string" 
myInt (x:xs) 
    | x == '-' = -1 * myInt xs 
    | otherwise = foldl convert 0 (x:xs) 
    where convert acc x 
      | x `elem` ['0'..'9'] = 10 * acc + digitToInt x 
      | otherwise   = error ("bad input: not an int - " ++ [x]) 

está bien:

Prelude> :l safeListFs.hs 
[1 of 1] Compiling Main    (safeListFs.hs, interpreted) 
Ok, modules loaded: Main. 

no puedo entender por qué los dos últimos en las abolladuras importan

+6

Esta pregunta es un buen ejemplo de por qué odio a la sintaxis de Haskell espacios en blanco; siempre me parece poco intuitivo en comparación con, digamos, Python. Lamentablemente, lo único que no me gusta * más * son los parches rizados que ensucian mi código. –

+9

http://book.realworldhaskell.org/read/defining-types-streamlining-functions.html#deftypes.offside La regla de fuera de juego me parece intuitiva. Solo tienes que dejar de pensar en bloques (como Python, que no tiene sentido en Haskell) y pensar en cambio en la continuación de una declaración o expresión. – ephemient

+2

Use un editor de texto inteligente y olvídese de las reglas de identación raras. –

Respuesta

24

Básicamente, porque Haskell toma nota de la columna en la que el primer carácter no-espacio después de where aparece (en este caso, el c de convert) y trata la siguiente líneas que comienzan en esa columna como nuevas definiciones dentro de la where.

Una línea que continúa con la definición de la línea anterior (como las protecciones |) debe sangrarse a la derecha de c, como en su versión que funciona.

Una línea sangrada a la izquierda de c (no hay ninguna en su ejemplo) estaría fuera del where (por ejemplo, el inicio de su próxima función de nivel superior).

Es la columna del primer carácter siguiente where que es crucial, aunque sea en una nueva línea:

where 
    convert acc x 
     | ... 
    anotherFunction x y 

    ^
+0

++ Maravilloso, no pude saltar a bordo de las otras respuestas, esto es muy parecido a lo que @arternave sugirió recientemente en un comentario, creo que esto explica el problema. –

6

Porque siempre debe sangrar las definiciones de las funciones. (En su caso, todo lo que comenzó en la misma columna en where se considera definición de "mismo nivel").

+0

Offtopic: Tengo un amigo cuyo nombre es Andrei Poluektov – artemave

+0

Todavía no entiendo esta respuesta, parece que en ambos ejemplos ** donde ** está justo debajo de la tubería ('|') y la sangría de la misma –

+0

@ Evan, 'donde' está apropiadamente sangrado en ambos casos. Son las tuberías después de 'dónde' que producen el error. Debido a que no están apropiadamente sangrados contra 'convertir' (en el primer ejemplo) – artemave

12

Un contexto anidado debe sangrarse más que el contexto adjunto (n> m). De lo contrario, L falla y el compilador debe indicar un error de diseño.

De http://www.haskell.org/onlinereport/syntax-iso.html.

Esto también sería un fracaso:

import Data.Char (digitToInt) 

myInt :: String -> Int 
myInt [] = error "bad input: empty string" 
myInt (x:xs) 
| x == '-' = -1 * myInt xs 
| otherwise = foldl convert 0 (x:xs) 
where convert acc x 
     | x `elem` ['0'..'9'] = 10 * acc + digitToInt x 
     | otherwise   = error ("bad input: not an int - " ++ [x]) 

Uh, soy malo en explicar las cosas. Hay un nuevo contexto después de la palabra clave where, porque puede especificar más de una función allí; recuerde que su programa comienza con module Main where implícito, así que creo que es lógico requerir que el cuerpo de la función se sancione, al igual que en el nivel del módulo (compilador espera que otro identificador en las columnas M y N, y los cuerpos de declaración sean más indentados).

fun = ... 
^ where fun' = ... 
M  ^
     N 
     fun'' = ... 
fun2 = ... 
+0

Aunque sus respuestas son obviamente correctas, no se dirigen a mi confusión particular. Lo cual se basaba en la suposición de que la sangría de alcance anidada comenzaría desde 'dónde'. Mientras que en realidad comienza desde el nombre de la función. – artemave

+0

Todavía no entiendo esto: hablen de por qué el ejemplo de OP que funciona, funciona y por qué el ejemplo que no funciona, no lo hace. –

+0

@Evan, mira mi edición. Así es como lo entiendo. –

Cuestiones relacionadas