2010-02-08 10 views
19

ten kod:Haskell "gdzie" wcięcie: dlaczego musi być wcięty w przeszłości identyfikator?

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

zawodzi:

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. 

Ale ta wersja:

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

jest ok:

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

Nie mogę zrozumieć, dlaczego te dwa ostatnie w wgniecenia mają znaczenie.

+6

to pytanie jest dobrym przykładem tego, dlaczego nienawidzę składnia spacje Haskell'a; zawsze wydaje mi się to nieintuicyjne w porównaniu do, powiedzmy, Pythona. Niestety, jedyną rzeczą, której nie lubię * więcej *, są brzydkie nawiasy klamrowe zaśmiecające mój kod. –

+9

http://book.realworldhaskell.org/read/defining-types-streamlining-functions.html#deftypes.offside Zasada "offside" wydaje mi się intuicyjna. Trzeba po prostu przestać myśleć o blokach (jak Python, co nie ma sensu w Haskell) i zamiast tego myśleć o kontynuacji deklaracji lub wyrażenia. – ephemient

+2

Użyj sprytnego edytora tekstu i zapomnij o dziwnych regułach identyfikacji. –

Odpowiedz

24

Zasadniczo, ponieważ Haskell zauważa kolumnę gdzie pierwszy znak zakaz przestrzeń where pojawia się po (w tym przypadku c z convert) i traktuje po linie rozpoczynające się w tej kolumnie jako nowe definicje wewnątrz where.

Linia, która kontynuuje definicję poprzedniej linii (np. Twoje osłony |), musi być wcięta po prawej stronie c, tak jak w wersji, która działa.

Linia wcięta po lewej stronie c (nie ma żadnych w twoim przykładzie) znajdowałaby się poza where (na przykład początek następnej funkcji najwyższego poziomu).

To kolumna pierwszego znaku następnego where, który jest bardzo ważny, nawet jeśli jest to w nowym wierszu:

where 
    convert acc x 
     | ... 
    anotherFunction x y 

    ^
+0

++ Cudownie, nie mogłem skoczyć na pokład innych odpowiedzi, jest to podobne do tego, co ostatnio zasugerował @arternave w komentarzu, myślę, że to wyjaśnia problem. –

6

Ponieważ powinieneś zawsze wciskać definicje funkcji. (W twoim przypadku wszystkie rzeczy rozpoczęte w tej samej kolumnie w where są uważane za definicję "tego samego poziomu").

+0

Offtopic: Mam znajomego o nazwisku Andrei Poluektov – artemave

+0

Wciąż nie rozumiem tej odpowiedzi, wydaje się, w obu przykładach ** gdzie ** jest tuż pod potokiem ("|") i wcięty w ten sam –

+0

@Evan, "where" jest właściwie wcięty w obu przypadkach. Jest to potok po "gdzie", który powoduje błąd. Ponieważ nie są odpowiednio wcięte w "konwertowanie" (w pierwszym przykładzie). – artemave

12

Zagnieżdżony kontekst musi być dalej wcięty niż otaczający kontekst (n> m). Jeśli nie, L zawodzi, a kompilator powinien wskazać błąd układu.

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

To też nie:

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, jestem zły na wyjaśniania rzeczy. Jest nowy kontekst po słowie kluczowym where, ponieważ możesz podać więcej niż jedną funkcję - pamiętaj, że twój program zaczyna się od niejawnego module Main where, więc myślę, że logiczne jest wymaganie wcięcia części funkcji, tak jak na poziomie modułu (kompilator oczekuje kolejnego identyfikatora na kolumnach M i N oraz kolejnych wcięć w ciałach deklaracji).

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

Chociaż twoje odpowiedzi są oczywiście poprawne, nie są one ukierunkowane na moje konkretne zamieszanie. Które zostało oparte na założeniu, że zagnieżdżenie zakresu zacznie się od "gdzie". Podczas gdy faktycznie zaczyna się od nazwy funkcji. – artemave

+0

Nadal nie rozumiem tego: porozmawiajmy o tym, dlaczego przykład OP działa, działa i dlaczego przykład, który nie działa, nie działa. –

+0

@Evan, zobacz moją edycję. Tak to rozumiem. –

Powiązane problemy