Как объясняет Уилл Несс в своем ответе, отступ не является вашей проблемой здесь - проблема в том, что вы пытаетесь использовать охрану (| …
) после оператора в блоке do
, но охранники могут только когда-либопоявляются между шаблонами и телами в (1) функциональных уравнениях:
function param1 param2
| guard1 = body1
| guard2 = body2
…
И (2) case
выражениях:
case expr of
pattern1
| guard1 -> body1
| guard2 -> body2
pattern2
| guard3 -> body3
…
Так что вы, вероятно, хотите вместо этого выражение if
.Что касается правил отступа, ваш код имеет правильные отступы, но вам не нужно столько пробелов, сколько вы используете: основные правила:
Некоторые ключевые слова, такие как do
, let
, where
и of
begin блоки компоновки
В этих блоках все должно иметь отступ после первого столбцапервая строка в блоке
Если выражение переносится на несколько строк, строки, следующие за первой, должны иметь отступ
Так что практическое правиловсегда работает добавление новой строки и отступа на некоторое количество пробелов (например, 2 или 4) после каждого такого ключевого слова:
readFile filename = do -- newline+indent to begin block
inputFile <- openFile filename ReadMode
readLines inputFile
hClose inputFile
readLines inputFile = do -- newline+indent to begin block
endof <- hIsEOF inputFile
if endof -- indent to continue if expression
then return ()
else do -- newline+indent to begin block
inpStr <- hGetLine inputFile
print inpStr
readLines inputFile
Альтернативный стиль - начать блок на той же строке, что иключевое слово макета;тогда все должно иметь то же выравнивание, что и эта строка:
readFile filename = do inputFile <- openFile filename ReadMode
readLines inputFile
hClose inputFile
readLines inputFile = do endof <- hIsEOF inputFile
if endof
then return ()
else do inpStr <- hGetLine inputFile
print inpStr
readLines inputFile
Я предпочитаю избегать этого стиля, потому что он приводит к глубокому отступу, который также зависит от ширины кода перед ним.
Оба этих стиля объединены со следующим кодом с явными разделителями, которые вы также можете написать сами, чтобы лучше понять, как работает макет:
readFile filename = do {
inputFile <- openFile filename ReadMode;
readLines inputFile;
hClose inputFile;
};
readLines inputFile = do {
endof <- hIsEOF inputFile;
if endof
then return ()
else do {
inpStr <- hGetLine inputFile;
print inpStr;
readLines inputFile;
};
};
Одна вещь, которая часто вводит людей в заблуждение, заключается в том, чтоlet
также вводит блок макета для определения нескольких привязок в одном блоке.Поэтому, если вы пишете оператор let
или выражение let
… in
… с длинным определением, то вам нужно либо написать его выровненным:
let example1 = some very long definition
that we need to wrap across lines
example2 = another binding for illustration
-- ^ everything must be aligned past this column
in example1 . example2
Или использовать тот же стиль перевода строки + отступачто касается всего остального:
let
example1 = some very long definition
that we need to wrap across lines
example2 = another binding for illustration
in example1 . example2
Наконец, для удобства, if x then return () else y
можно записать unless x y
или when (not x) y
, используя unless
или when
из Control.Monad
:
import Control.Monad (unless)
…
endof <- hIsEOF inputFile
unless endof $ do
inpStr <- hGetLine inputFile
print inpStr
readLines inputFile
Кроме того, вы можете увидеть $
, опущенный перед do
(и другие ключевые слова блока, такие как case
и \
) в коде, который включает расширение BlockArguments
:
{-# LANGUAGE BlockArguments #-}
import Control.Monad (unless)
…
endof <- hIsEOF inputFile
unless endof do -- no need for $
inpStr <- hGetLine inputFile
print inpStr
readLines inputFile