2010-02-08 5 views
19

Этот код:Haskell «где» отступы: почему он должен быть отступом прошлого идентификатора?

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

Сбой:

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. 

Но эта версия:

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

нормально:

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

Я не могу понять, почему те, два последних вмятины имеют значение.

+6

Этот вопрос является хорошим примером того, почему я ненавижу пробельные синтаксис в Haskell; он всегда кажется мне неинтуитивным по сравнению с, скажем, Python. К сожалению, единственное, что мне не нравится * больше *, - это уродливые фигурные скобки, засоряющие мой код. –

+9

http://book.realworldhaskell.org/read/defining-types-streamlining-functions.html#deftypes.offside Правило офсайда кажется интуитивным для меня. Вам просто нужно перестать думать о блоках (например, Python, что не имеет смысла в Haskell) и вместо этого подумать о продолжения объявления или выражения. – ephemient

+2

Используйте умный текстовый редактор и забудьте о странных правилах идентификации. –

ответ

24

В основном, потому что Haskell отмечает колонку, где первый не пробел после where появляется (в данном случае, c из convert) и обрабатывает следующие строки, начинающиеся в этой колонке в качестве новых определений внутри where.

Линия, которая продолжает определение предыдущей строки (например, ваши защитные устройства |), должна быть отступом справа от c, как в вашей версии, которая работает.

Строка с отступом слева от c (их в вашем примере нет) будет за пределами where (например, начало следующей функции верхнего уровня).

Это столбец первого символа следующего where, что имеет решающее значение, даже если он находится на новой строке:

where 
    convert acc x 
     | ... 
    anotherFunction x y 

    ^
+0

++ Замечательно, я не мог спрыгнуть на борт других ответов, это очень похоже на то, что @arternave предложил недавно в комментарии, я думаю, это объясняет проблему. –

6

Потому что вы всегда должны указывать определения функции отступа. (В вашем случае все вещи, начатые в одном столбце в where, считаются «одинаковым уровнем»).

+0

Оффтопик: У меня есть друг, которого зовут Андрей Полуэктов – artemave

+0

Я до сих пор не понимаю этого ответа, кажется, в обоих примерах **, где ** находится прямо под трубой ('|') и отступом то же самое –

+0

@Evan, «где» в обоих случаях должным образом отступом. Это трубы после «где», которые вызывают ошибку. Поскольку они не имеют должным образом отступ от «конвертирования» (в первом примере) – artemave

12

Вложенный контекст должен иметь дополнительный отступ, чем охватывающий контекст (n> m). Если нет, L терпит неудачу, и компилятор должен указать ошибку компоновки.

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

Это также не:

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

Мм, я плохо объяснить вещи. Появится новый контекст после ключевого слова where, потому что вы можете указать несколько функций там - помните, что ваша программа начинается с неявного module Main where, поэтому я считаю логичным требовать, чтобы тело функции было отступом, как на уровне модуля (компилятор ожидает другого идентификатора в столбцах M и N, а тела декларации должны быть отступы).

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

Хотя ваши ответы, очевидно, правильны, это не нацелено на мою конкретную путаницу. Это было основано на предположении, что вложенный отступ области будет начинаться с «где». В то время как он фактически начинается с имени функции. – artemave

+0

Я до сих пор не понимаю этого: скажите, почему пример OP, который работает, работает и почему пример, который не работает, не работает. –

+0

@Evan, см. Мое редактирование. Вот как я это понимаю. –

Смежные вопросы