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