Control.Monad.Failable
Yet another “error” handling monad (class)
This library provides a ‘Failable’ error monad class to unify failure across monads and
transformers most commonly used to implement pipelines that can fail.
But.. don’t we have ‘MonadFail’, ‘MonadThrow’, ‘MonadError’,.. and the true haskeller should be
using ‘Alternative’ anyway!
I am sure a lot of ink has been spilled in forums and around water coolers all around the world,
debating the merits and fallacies of one approach or the other. The reason for this package is not
to participate in this discussion but rather to provide a simple no nonsense means of signaling a
computation “failure” in those monads that provide the inherent means to do so, and to do it in a
consistent manner
Usage
data FooError = NotImplemented deriving (Typeable, Show)
instance Exception FooError
foo :: (Failable m) => m Int
foo = failure NotImplemented
Now, if one called foo
in a Maybe monad
:
> foo :: Maybe Int
> Nothing
the failure is then conveyed by returning Nothing
as per definition of the Maybe
monad. Now in the case of the Either SomeException
monad:
> foo :: Either SomeException Int
> Left NotImplemented
but what if we are working in the IO
monad?
> foo :: IO Int
> * * * Exception: NotImplemented
In this case, the failure can only be conveyed by throwing an IO exception.
Now, the point where Failable
diverges from say MonadThrow
for example is when it comes to monad
transformers. For example:
> runMaybeT foo :: IO (Maybe Int)
Would throw an Exception: NotImplemented
if it was implemented in a MonadThrow
context. Since
the reason d’etre for the runMaybeT is to provide the underlying monad (transformer) with Maybe
like behaviour, i.e. have Nothing
be returned in case of aborting the Maybe
pipeline so to speak, then throwing an exception defeats IMHO the purpose of using MaybeT
in the first place. So, in the
case of Failable
:
> runMaybeT foo :: IO (Maybe Int)
> Nothing
And the same thing applies to runExceptT
etc.