Правильные правила отступов с использованием охраны - PullRequest
2 голосов
/ 15 апреля 2019

Я посмотрел на вопросы, касающиеся отступов, которые не помогли.Мой отступ также выглядит правильно, но в соответствии с компилятором это не так.Что такое правильный отступ и каковы правила?

readFile filename = do 
                    inputFile <- openFile filename ReadMode
                    readLines inputFile
                    hClose inputFile

readLines inputFile = 
        do endof <- hIsEOF inputFile 
            | endof = return() 
            | otherwise = do 
                          inpStr <- hGetLine inputFile
                          print inpStr
                          readLines inputFile

Использование всех пробелов и без табуляции.Ошибка: «ошибка разбора на входе» || endof = return () "

Ответы [ 2 ]

4 голосов
/ 15 апреля 2019

Вы можете реструктурировать свой код для этого, как

readLines :: Handle -> IO ()
readLines inputFile  =  g =<< hIsEOF inputFile
    where                  -- hIsEOF :: Handle -> IO Bool
      g endof
            | endof = return () 
            | otherwise = do 
                          inpStr <- hGetLine inputFile
                          print inpStr
                          readLines inputFile

Защитные элементы, | ..., принадлежат определениям функций или регистрам Они не могут появляться в блоке do сами по себе.

g =<< hIsEOF inputFile это более короткий способ записи

readLines inputFile  =  do {  endof <- hIsEOF inputFile
                           ;  g endof
                           }
    where
      g endof
            | endof = .....

Но более простой вариант - просто использовать if ... then ... else ... в блоке do:

readLines inputFile = 
        do { endof <- hIsEOF inputFile 
           ; if endof 
                then return() 
                else do { inpStr <- hGetLine inputFile
                        ; print inpStr
                        ; readLines inputFile
                        }}

Еще один использует LambdaCase , чтобы встроить определение g:

readLines :: Handle -> IO ()
readLines inputFile  =  hIsEOF inputFile >>=
    (\ case { True -> return () 
            ; _    -> do 
                          inpStr <- hGetLine inputFile
                          print inpStr
                          readLines inputFile })

И в кейсах могут быть охранники (хотя здесь они нам не нужны).

1 голос
/ 16 апреля 2019

Как объясняет Уилл Несс в своем ответе, отступ не является вашей проблемой здесь - проблема в том, что вы пытаетесь использовать охрану (| …) после оператора в блоке 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 или выражение letin… с длинным определением, то вам нужно либо написать его выровненным:

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
...