People who know me in person will likely have heard me express my love for Haskell on grounds that it is hard for things to ‘go wrong’ at runtime, and so they might be surprised to hear I’m reviewing a library called
errors. One of the major reasons it’s hard to go wrong is due to the rich type system - if things can go wrong you usually encode that in the types. However, being explicit about this can produce fairly verbose code when you have to deal with all the edge cases.
This is where Gabriel Gonzalez’s
errors library comes in.
errors provides you with a comprehensive set of combinators to easily work with the two main error handing types:
Either. I personally find the
hush combinators very useful - this pair of combinators make it a breeze to jump between Maybe and Either as you need.
askAge :: IO (Either String Int) = note "Invalid input" . readMay <$> getLineaskAge
I’m making use of
readMay from the
Safe library (which is exported by
errors), and then using
note to convert the possible failure into something more descriptive. It’s simple code, but it resonates a lot with me because it’s very Haskell-y - we’re building up a complex program out of lots of small building blocks.
As hinted to before,
errors also exports a lot of safe alternatives to functions in the Haskell prelude that are partial and can throw runtime exceptions. I assume this is the case in the prelude due to ease of use but now that you have all these combinators you have no excuse!
errors also provides the module
Control.Error.Script module which aids writing command line scripts. Programming command line scripts can be tricky because you usually want to get the task done quickly yet robustly, and as the command line brings you very close to the user there’s often a lot that can go wrong. Here’s a (not at all contrived) script which optimistically tries to print the 5th line from any file the user wishes:
main :: IO () = runScript $ do main <- hoistEither =<< note usage . headMay <$> scriptIO getArgs filename $ readFile filename >>= putStrLn . line5 scriptIO where = "Usage: line5 <file>" usage = lines l !! 5 line5 l
I hacked this out very quickly - and there’s a mix of me being cautious (using
headMay) and outright dangerous (
lines l !! 5).
scriptIO lets me lift IO actions into the
Script monad - giving slightly better error diagnostics.
Gabriel has written about
errors on his own blog in detail so if you’re interested I can recommend having a read there. Gabriel’s blog is interesting in general, often providing an accessible and practical introduction to fairly abstract topics, such as free monads. If you’re a fan of this side of Haskell, I definitely recommend subscribing!
In a similar vain to this post, I wrote about error handling using
EitherT earlier this year. The post seemed successful when I initially published it so if you missed it the first time round, check out my post “In Praise of EitherT”.
You can contact me via email at firstname.lastname@example.org or tweet to me @acid2. I share almost all of my work at GitHub. This post is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License.