The Simplest MonadFail Instance
Tags: haskell
Published: 2020-09-02 10:28:54

The Japanese version is at [はてなブログ](https://kakkun61.hatenablog.com/entry/2020/09/02/%E4%B8%80%E7%95%AA%E7%B0%A1%E5%8D%98%E3%81%AA_MonadFail_%E3%82%A4%E3%83%B3%E3%82%B9%E3%82%BF%E3%83%B3%E3%82%B9).

----

# Introduction

After `fail` was removed from `Monad` a long time ago, I prefer to type computations that may fail to `MonadFail`.

```haskell
foo :: MonadFail m => m a
```

This allows you to instantiate `m` to `IO` within the `IO` context, and to instantiate it to `Maybe` within the pure context for example.

```haskell
-- within IO context
foo :: IO a

-- within pure context
foo :: Maybe a
```

It is not happy that `Maybe` discards the failure messages. So should we use `Either`? `Either` is not actually a `MonadFail` instance. There was [a proposal about this](https://gitlab.haskell.org/ghc/ghc/-/issues/12160) which was rejected because of the following reason.

> the instances "get in the way of a user who wants to treat the parameter uniformly"

That being so I wanted the following `Result` type as the simplest `MonadFail` instance.

```haskell
newtype Result a = Result (Either String a)

instance MonadFail Result where
  fail = Left
```

There is actually an equivalent of this but it is deprecated. It is `ErrorT` of the mtl package.

# either-result package

I released [the either-result package](https://hackage.haskell.org/package/either-result) which contains `Result` and some functions.

https://hackage.haskell.org/package/either-result

Actually `Result` is an alias of `ResultT Identity` and `ResultT` is a newtype of `ExceptT` of the transformers package.

```haskell
type Result a = ResultT Identity a

newtype ResultT m a = ResultT (ExceptT String m a)
```

What is the difference between `ResultT` and `ExceptT` is about `MonadFail` instances. On `ResultT` `fail` wraps a message with `Left`, while on `ExceptT` it calls `fail` of its base monad. Because of it, on `ResultT` its base monad is requested to be just `Monad`, but on `ExceptT` its one is requested to be `MonadFail`.

```haskell
instance Monad m => MonadFail (ResultT m) where …

instance MonadFail m => MonadFail (ExceptT e m) where …
```

You can use `throwError` and `catchError` because `ResultT` is also `MonadError` instance of the mtl package.

# What about the exceptions package?

There is `MonadThrow` type class, isn't it? Yes, the exceptions package has `MonadThrow` and `MonadCatch` type classes. These requests something thrown and caught to be `Exception` instances. In my opinion, if you want to recognise thrown and caught things by their type, you should use them. And if you want just messages, use `MonadFail`.

```haskell
class Monad m => MonadThrow m where
  throwM :: Exception e => e -> m a

class MonadThrow m => MonadCatch m where
  catch :: Exception e => m a -> (e -> m a) -> m a

class Monad m => MonadFail m where
  fail :: String -> m a
```

# Conclusion

- on defining, type computations which may fail to `MonadFail m => m a`
- on using, use it as `IO a` within `IO` context for example
- on using, use it as `Result a` within a pure context
- add star to [the GitHub repository](https://github.com/kakkun61/either-result) 😉