问题
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
回答1:
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.
回答2:
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.
回答3:
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
回答4:
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.
来源:https://stackoverflow.com/questions/4513396/do-construct-in-haskell