Question

I'm trying to learn Haskell and want to write a small program which prints the content of a file to the screen. When I load it into GHCi I get the following error:

The last statement in a 'do' construct must be an expression

I know this question has be asked already here: Haskell — “The last statement in a 'do' construct must be an expression”.

Even though my code is very similar I still can't figure out the problem. If anyone could point out the problem to me I'd be very thankful.

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
Was it helpful?

Solution

Your indentation is broken. These are better:

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

By having if further indented than end <- hIsEof handle, it was actually a line continuation, not a subsequent action in the do. Similarly, the fact that you had putStrLn line less indented than line <- hGetLine handle means that the do (inside the else) ended there.

OTHER TIPS

There are seveal issues. First, the if is indented too far - end <- ... is assumed to be the last line of the do. Unindent...

next issue comes up. Same error message, only at line 18. This time, line 19 and 20 are not indented deeply enough (they aren't parsed as part of the do). Indent (looks nicer anyway, since it all lines up now)... next error message. The good news is, it's not an indentation error this time and the fix is again trivial.

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 }

The fix is inh <- openFile (head args) ReadMode. If you want a more detailed explanation of why/how your version is incorrect, or what the error means, let me know and I'll edit.

You wrote this:

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

But it is probably nicer like this:

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

You can always use explicit bracketing with { ; } to never have to worry about this whitespace foolishness.

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

would have been totally fine (as in, not cause the error).

I/O is dealt with through the special "do" language, in Haskell. It should be embraced. That it is actually implemented via monads is an implementational detail.

To clarify: I don't think braces are better, I think they should go together with a nice and consistent indentation. Braces give us nice and immediate visual clues as to the code's structure. Wild indentation will of course be a pointless distraction most of the time. But also, braces give us a guarantee for the working code, and relieve us from the pointless worries of whitespace accidents. They remove this brittleness.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top