I'm learning Haskell (from LYAH) and just starting to understand monads. I am trying to write a command line program which parses the command line, opens and reads a file (or files) whose paths are specified as parameters, does some processing, and outputs the result. Of itself this is not too difficult, but I am trying to figure out how to do the error handling in a clear, idiomatic manner. Using Either as a monad in a do block seems a good way of handling errors in pure functional code, but the top level of my program has to do IO, which can also fail with errors, and the top level main function is of type IO () not of type Either a b, add I can't figure out how to combine them.
To make my issue clearer, here are some examples of ways I've tried to handle it, but I'm not happy with any of them.
Please note: Earlier this question was marked as a duplicate of one about handling many levels of indentation but that is not what I am asking - I'm asking about how to handle errors in an IO context. I've re-ordered the examples to make that clearer.
- die on error:
main = do
args <- getArgs
filename <- case parseArgs args of
Left err -> die $ "ERROR: " ++ err
Right f -> return f
loadResult <- loadFile filename
contents <- case loadResult of
Left err -> die $ "ERROR: " ++ err
Right c -> return c
res <- case process contents of
Left err -> die $ "ERROR: " ++ err
Right r -> return r
printReport filename res
This works but it is inelegant and inflexible. There's no chance of doing cleanup or moving on to another file after failure.
- The either chain:
main = do
args <- getArgs
case parseArgs args of
Left err -> putStrLn $ "ERROR: " ++ err
Right filename -> do
loadResult <- loadFile filename
case loadResult of
Left err -> putStrLn $ "ERROR: " ++ err
Right contents ->
case process contents of
Left err -> putStrLn $ "ERROR: " ++ err
Right res -> printReport filename res
This is nasty because it keeps stepping to the right, and is not practical for longer sequences.
- I understand that exceptions might be another possibility, but LYAH discourages their use and suggests using
IO (Either a b)instead. However, I can't see how to do that.
What would be the idiomatic way to deal with this?