конструкция do в Haskell - PullRequest
       47

конструкция do в Haskell

1 голос
/ 22 декабря 2010

Я пытаюсь изучить Haskell и хочу написать небольшую программу, которая выводит содержимое файла на экран. Когда я загружаю его в GHCi, я получаю следующую ошибку:

Последний оператор в конструкции do должен быть выражением

Я знаю, что этот вопрос уже задавался здесь: Haskell - «Последнее утверждение в конструкции do должно быть выражением» .

Хотя мой код очень похож, я все еще не могу понять проблему. Если бы кто-нибудь мог указать мне на проблему, я был бы очень благодарен.

module Main (main) where

import System.IO
import System(getArgs)

main :: IO()
main = do
    args <- getArgs
    inh <- openFile $ ReadMode head args
    printFile inh
    hClose inh

printFile :: Handle -> IO ()
printFile handle = do
    end <- hIsEOF handle
        if end
            then return ()
            else do line <- hGetLine handle
                putStrLn line
                printFile handle

Ответы [ 4 ]

5 голосов
/ 22 декабря 2010

Ваш отступ нарушен. Это лучше:

printFile :: Handle -> IO ()
printFile handle = do
    end <- hIsEOF handle
    if end
        then return ()
        else do line <- hGetLine handle
                putStrLn line
                printFile handle

printFile :: Handle -> IO ()
printFile handle = do
    end <- hIsEOF handle
    if end
        then return ()
        else do
            line <- hGetLine handle
            putStrLn line
            printFile handle

Имея отступ if дальше, чем end <- hIsEof handle, это фактически было продолжением строки, а не последующим действием в do. Точно так же тот факт, что у вас было putStrLn line меньше отступа, чем line <- hGetLine handle, означает, что do (внутри else) закончилось там.

4 голосов
/ 22 декабря 2010

Есть серьезные проблемы. Во-первых, if имеет слишком большой отступ - end <- ... считается последней строкой do. Unindent ...

появляется следующая проблема. То же сообщение об ошибке, только в строке 18. На этот раз строки 19 и 20 не имеют глубоких отступов (они не анализируются как часть do). Отступ (в любом случае выглядит лучше, так как все теперь выстраивается) ... следующее сообщение об ошибке. Хорошая новость в том, что на этот раз это не ошибка отступа, и исправление снова тривиально.

test.hs:9:22:
    Couldn't match expected type `([a] -> a) -> [String] -> FilePath'
           against inferred type `IOMode'
    In the second argument of `($)', namely `ReadMode head args'
    In a stmt of a 'do' expression:
        inh <- openFile $ ReadMode head args
    In the expression:
        do { args <- getArgs;
             inh <- openFile $ ReadMode head args;
             printFile inh;
             hClose inh }

Исправление inh <- openFile (head args) ReadMode. Если вам нужно более подробное объяснение, почему / как ваша версия неверна или что означает ошибка, дайте мне знать, и я отредактирую.

1 голос
/ 15 июня 2018

Вы всегда можете использовать явные скобки с { ; }, чтобы никогда не беспокоиться об этой глупости.

printFile :: Handle -> IO ()
printFile handle = do {
    end <- hIsEOF handle ;
        if end
            then return ()
            else do { line <- hGetLine handle ;
                putStrLn line ;
                printFile handle }}

было бы полностью нормально (как, не вызывать ошибку).

Ввод / вывод обрабатывается через специальный язык "do" в Haskell. Это следует принять. То, что это на самом деле реализовано с помощью монад, является деталью реализации.

Чтобы уточнить: я не думаю, что скобки лучше, я думаю, что они должны идти вместе с красивым и последовательным отступом. Скобки дают нам хорошие и непосредственные визуальные подсказки относительно структуры кода. Дикие отступы, конечно, будут бесполезным отвлечением большую часть времени. Кроме того, фигурные скобки дают нам гарантию на работоспособность кода и избавляют нас от бессмысленных забот о случайных ситуациях. Они устраняют эту хрупкость.

1 голос
/ 23 декабря 2010

Вы написали это:

main :: IO()
main = do
    args <- getArgs
    inh <- openFile $ ReadMode head args
    printFile inh
    hClose inh

Но это, вероятно, лучше, как это:

main :: IO()
main = do
    args <- getArgs
    withFile (head args) ReadMode printFile
...