failable-1.0.0.0: README.md
# 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
```haskell
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.
## The IO problem
One of the most common complaints about error monads is that they erroneously give the impression that if the user deals with the returned failed condition (i.e. `Nothing` or `Left <SomeError>` for `Maybe(MaybeT)` or `Either(ExceptT)` respectively) the job is done and the code is now "safe", when in reality all one has done is opened up an additional error "path" on top of IO exceptions. Regarldess of one's position on IO exceptions, truth is they are not going to go away.. probably _ever_. So one has to find a way to live with them
in the best possible manner. To this effect, this library offers a utility function `failableIO`. This function can be used if the Failable monad is also an instance of MonadIO and it lifts an IO operation into the monad but in the event of an IO error, it returns this as a failure in the right context. So for example:
```haskell
foo :: (Failable m, MonadIO m) => m ()
foo = do
failableIO $ do
txt <- readFile "foo.txt"
putStrLn txt
```
> runExceptT foo
> Left foo.txt: openFile: does not exist (No such file or directory)
> runMaybeT foo
> Nothing
but if ran directly on IO:
> foo
> *** Exception: foo.txt: openFile: does not exist (No such file or directory)
IMHO this is an improvement from having `foo` fail with an IO exception _or_ a failure value depending on the context.