title |
---|
24 Days of Hackage: errors |
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)
askAge = note "Invalid input" . readMay <$> getLine
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 ()
main = runScript $ do
filename <- hoistEither =<< note usage . headMay <$> scriptIO getArgs
scriptIO $ readFile filename >>= putStrLn . line5
where
usage = "Usage: line5 <file>"
line5 l = lines l !! 5
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".