I was recently working on a small little project - a client API for the ListenBrainz project. Most of the details aren’t particularly interesting - it’s just a HTTP client library to a REST-like API with JSON. For the implementation, I let Servant and aeson do most of the heavy lifting, but I got stuck when considering what final API to give to *my* users.

Obviously, interacting with ListenBrainz requires some sort of IO so whatever API I will be offering has to live within some sort of monad. Currently, there are three major options:

*Supply an API targetting a concrete monad stack.*Under this option, our API would have types such as

where

`M`

is some*particular*monad (or monad transformer).*Supply an API using type classes*This is the

`mtl`

approach. Rather than choosing which monad my users have to work in, my API can be polymorphic over monads that support accessing the ListenBrainz API. This means my API is more like:*Use an extensible effects framework.*Extensible effects are a fairly new entry, that are something of a mix of the above options. We target a family of concrete monads -

`Eff`

- but the extensible effects framework lets our effect (querying ListenBrainz) seamlessly compose with other effects. Using`freer-effects`

, our API would be:

So, which do we choose? Evaluating the options, I have some concerns.

For option one, we impose pain on all our users who want to use a different monad stack. It’s unlikely that you’re application is going to be written soley to query ListenBrainz, which means client code becomes littered with `lift`

. You may write that off as syntatic, but there is another problem - we have committed to an interpretation strategy. Rather than describing API calls, my library now skips directly to prescribing how to run API calls. However, it’s entirely possible that you want to intercept these calls - maybe introducing a caching layer or additional logging. Your only option is to duplicate my API into your own project and wrap each function call and then change your program to use your API rather than mine. Essentially, the program itself is no longer a first class value that you can transform.

Extensible effects gives us a solution to both of the above. The use of the `Member`

type class automatically reshuffles effects so that multiple effects can be combined without syntatic overhead, and we only commit to an interpretation strategy when we actually run the program. `Eff`

is essentially a *free monad*, which captures the syntax tree of effects, rather than the result of their execution.

Sounds good, but extensible effects come with (at least) two problems that make me hesistant: they are experimental and esoteric, and it’s unclear that they are performant. By using *only* extensible effects, I am forcing an extensible effects framework on my users, and I’d rather not dictate that. Of course, extensible effects can be composed with traditional monad transformers, but I’ve still imposed an unnecessary burden on my users.

So, what do we do? Well, as Old El Paso has taught us: why don’t we have both?

It’s trivial to actually support both a monad transformer stack *and* extensible effects by using an `mtl`

type class. As I argue in Monad transformers, free monads, mtl, laws and a new approach, I think the best pattern for an `mtl`

class is to be a monad homomorphism from a program description, and often a free monad is a fine choice to lift:

But what about `f`

? As observed earlier, extensible effects are basically free monads, so we can actually share the same implementation. For `freer-effects`

, we might describe the ListenBrainz API with a GADT such as:

```
data ListenBrainzAPICall returns where
GetListens :: ... -> ListenBrainzAPICall Listens
SubmitListens :: ... -> ListenBrainzAPICall ()
```

However, this isn’t a functor - it’s just a normal data type. In order for `Free f a`

to actually be a monad, we need `f`

to be a functor. We could rewrite `ListenBrainzAPICall`

into a functor, but it’s even easier to just fabricate a functor for free - and that’s exactly what `Coyoneda`

will do. Thus our `mtl`

type class becomes:

```
class Monad m => MonadListenBrainz m where
liftListenBrainz :: Free (Coyoneda ListenBrainzAPICall) a -> m a
```

We can now provide an implementation in terms of a monad transformer:

```
instance Monad m => MonadListenBrainz (ListenBrainzT m)
liftListenBrainz f =
iterM (join . lowerCoyoneda . hoistCoyoneda go)
where
go :: ListenBrainzAPICall a -> ListenBrainzT m a
```

or extensible effects:

```
instance Member ListenBrainzAPICall effs => MonadListenBrainz (Eff effs) where
liftListenBrainz f = iterM (join . lowerCoyoneda . hoistCoyoneda send) f
```

or maybe directly to a free monad for later inspection:

For the actual implementation of performing the API call, I work with a concrete monad transformer stack:

which both my extensible effects “run” function calls, or the `go`

function in the `iterM`

call for `ListenBrainzT`

’s `MonadListenBrainz`

instance.

In conclusion, I’m able to offer my users a choice of either:

- a traditional monad transformer approach, which doesn’t commit to a particular intepretation strategy by using an
`mtl`

type class - extensible effects

All without extra syntatic burden, a complicated type class, or duplicating the implementation.

You can see the final implemantion of `listenbrainz-client`

here.

The ReaderT design pattern has been mentioned recently, so where does this fit in? There are two options if we wanted to follow this pattern:

- We require a HTTP
`Manager`

in our environment, and commit to using this. This has all the problems of providing a concrete monad transformer stack - we are committing to an interpretation. - We require a family of functions that explain how to perform each API call. This kind of like a van Laarhoven free monad, or really just explicit dictionary passing. I don’t see this really gaining much on abstracting with type classes.

I don’t feel like the ReaderT design pattern offers anything that isn’t already dealt with above.

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.

I accept Bitcoin donations: `14SsYeM3dmcUxj3cLz7JBQnhNdhg7dUiJn`

. Alternatively, please consider leaving a tip on