I'm trying to add a simple handler to the readFile
function:
readHandler :: IOError -> IO ()
readHandler e
| isDoesNotExistError e = putStrLn "The file does not exist"
| otherwise = putStrLn "Something went wrong"
main = do
// stop executing if there is no file
contents <- (readFile "path.xml") `catch` readHandler
// generates [(x,y),(x,y)]
coordinates = parseXML contents
// I want to do something with the coordinates
nextFunction coordinates
When I try to compile this I get the error:
Couldn't match type `()' with `[Char]'
Expected type: IOError -> IO String
Actual type: IOError -> IO ()
In the second argument of `catch', namely `readHandler'
In a stmt of a 'do' block:
contents <- (readFile "path") `catch` readHandler
In the expression:
do { contents <- (readFile "path") `catch` readHandler;
putStrLn contents }
Thus readHandler :: IOError -> IO ()
should be readHandler :: IOError -> IO String
.
But this way I can't print the error message.
How should I solve this problem?
What should the type of catch (readFile "path") readHandler
be?
Obviously if the file exists, we'd want it to be a String
, catch
shouldn't change it's type, so somehow we have to produce a String
no matter what. Since if an exception is thrown, readHandler
is run, it must also produce a string.
In this way catch
is acting like a very sophisticated if
expression :) However, it's less than ideal since we don't want to keep running our function with some random String
that didn't come from the file.
Instead we could do something like
main = handle readHandler $ do
...
Since now we just have to produce an IO ()
, trivial.
If this doesn't float your boat for whatever reason, another sane choice would be to transform that irritating exception to a more pleasant Either
main = do
strOrExc <- try $ readFile "foo"
case strOrExc of
Left except -> print except
Right contents -> putStrLn contents
and of course you could handle this Exception e => Either e a
in whatever way brings you happiness.
There is of course a final alternative, bring the whole program to a screeching halt right then and there. We can do this just by changing readHandler
import System.Exit
readHandler :: IOError -> IO a
readHandler = putStrLn "Buckle your seatbelts" >> exitFailure
catch has type
catch :: Exception e => IO a -> (e -> IO a) -> IO a
So if the first parameter of catch is IO String
, then the second argument (which is a function), has to return that either.
You could change your readHandler like so:
readHandler :: IOError -> IO String
readHandler e
| isDoesNotExistError e = do putStrLn "The file does not exist" ; return ""
| otherwise = do putStrLn "Something went wrong" ; return ""
But I am not sure, whether that's a wanted "side effect" (returning an empty string).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With