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: Maybe
and Either
. I personally find the note
and 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 <$> getLine askAge
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 ollie@ocharles.org.uk 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.