2010-02-08 10 views
19

Questo codice:Haskell "where" indentation: perché deve essere indentato l'identificatore passato?

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]) 

non riesce:

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. 

Ma questa versione:

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]) 

è ok:

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

non riesco a capire il motivo per cui quelli due ultimi a le ammaccature contano.

+6

Questa domanda è un buon esempio del perché io odio la sintassi spazi di Haskell; mi sembra sempre non intuitivo rispetto a, diciamo, Python. Sfortunatamente, l'unica cosa che non mi piace di più è brutta parentesi graffe che sporca il mio codice. –

+9

http://book.realworldhaskell.org/read/defining-types-streamlining-functions.html#deftypes.offside La regola del fuorigioco mi sembra intuitiva. Devi solo smettere di pensare ai blocchi (come Python, che non ha senso in Haskell) e pensare invece alle continuazioni di una dichiarazione o espressione. – ephemient

+2

Utilizza un editor di testo intelligente e dimentica le strane regole di identificazione. –

risposta

24

Fondamentalmente, perché Haskell osserva la colonna in cui il primo carattere non spazio dopo where appare (in questo caso, il c di convert) e tratta seguente linee che iniziano in quella colonna come nuove definizioni all'interno del where.

Una riga che continua la definizione della riga precedente (ad esempio le protezioni |) deve essere rientrata a destra di c, come nella versione che funziona.

Una riga rientrata a sinistra di c (non ce ne sono nel tuo esempio) sarebbe all'esterno dello where (ad esempio, l'inizio della prossima funzione di primo livello).

E 'la colonna del primo carattere che segue where che è fondamentale, anche se è in una nuova riga:

where 
    convert acc x 
     | ... 
    anotherFunction x y 

    ^
+0

++ Meraviglioso, non potrei saltare a bordo delle altre risposte, questo è molto simile a ciò che @arternave ha suggerito di recente in un commento, penso che questo spieghi il problema. –

6

Perché si dovrebbero sempre rientrare le definizioni di funzione. (Nel tuo caso, tutte le cose iniziate nella stessa colonna in where sono considerate definizioni di "stesso livello").

+0

Offtopic: Ho un amico il cui nome è Andrei Poluektov – artemave

+0

Ancora non capisco questa risposta, sembra in entrambi gli esempi ** dove ** è proprio sotto la pipe ('|') e rientrato lo stesso –

+0

@Evan, 'dove' è correttamente rientrato in entrambi i casi. Sono pipe dopo "where" che producono l'errore. Perché non sono debitamente rientrati rispetto a 'convert' (nel primo esempio) – artemave

12

Un contesto nidificato deve essere ulteriormente rientrato rispetto al contesto di inclusione (n> m). In caso contrario, L non riesce e il compilatore dovrebbe indicare un errore di layout.

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

Ciò consentirebbe anche di fallire:

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, sto male a spiegare le cose. C'è un nuovo contesto dopo la parola chiave where, perché è possibile specificare più di una funzione in là - ricorda che il tuo programma inizia con implicito module Main where, quindi penso che sia logico richiedere che il corpo della funzione sia rientrato, proprio come al livello del modulo (compilatore si aspetta un altro identificatore sulle colonne M e N, e i corpi delle dichiarazioni devono essere ulteriormente rientrati).

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

Sebbene le tue risposte siano ovviamente corrette, non mirano alla mia particolare confusione. Il che si basava sul presupposto che l'indentazione dell'ambito annidato sarebbe iniziata da "dove". Mentre in realtà inizia dal nome della funzione. – artemave

+0

Ancora non lo capisco: parla del perché l'esempio dell'OP funziona, funziona e perché l'esempio che non funziona, no. –

+0

@Evan, vedere la mia modifica. Questo è il modo in cui lo capisco. –

Problemi correlati