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

Я пытаюсь изучить 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 ответа

Решение

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

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) закончилась там.

Есть серьезные проблемы. Во-первых, 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, Если вы хотите получить более подробное объяснение, почему / как ваша версия неверна или что означает ошибка, дайте мне знать, и я отредактирую.

Это ты написал:

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

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

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

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

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

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

Другие вопросы по тегам