# cats-mtl Design

Overall, cats-mtl has similar design to cats: instances package, syntax package, implicits package.

There’s also a hierarchy package, because cats-mtl uses a different typeclass encoding than cats to avoid implicit ambiguity.

The problem looks like this:

```
import cats._
type Err
type Log
def f[F[_]: MonadError[?, Err]: MonadWriter[?, Log]]: Unit = {
// implicitly[Monad[F]] // ambiguity between derived monads from MonadError and MonadWriter
// 1.pure[F].flatMap(_ => 1.raise) // same ambiguity affects MonadSyntax
()
}
```

The cause is that the implicit conversions `MonadError[F, Err] => Monad[F]`

and
`MonadWriter[F, Log] => Monad[F]`

are the same priority. If those conversions
were different priorities, the issue would be fixed, and that’s exactly what cats-mtl’s encoding does.

Similarly to the Scato project, instead of inheritance the transformer classes in cats-mtl use aggregation. Mtl classes thus contain instances of their superclasses.

## Motivation

The motivation for cats-mtl’s existence can be summed up in a few points:

- using subtyping to express typeclass subclassing results in implicit ambiguities, and doing it another way would result in a massive inconsistency inside cats if only done for MTL classes. for a detailed explanation, see Adelbert Chang’s article here.
- most MTL classes do not actually require
`Monad`

as a constraint for their laws. cats-mtl weakens this constraint to`Functor`

or`Applicative`

whenever possible, with the result that there’s now a notion of a`Functor`

transformer stack and`Applicative`

transformer stack in addition to that of a`Monad`

transformer stack. - the most used operations on
`MonadWriter`

and`MonadReader`

are`tell`

and`ask`

, and the other operations severely restrict the space of implementations despite being used much less. To fix this`Listen`

and`Local`

are subclasses of`Tell`

and`Ask`

, which have only the essentials.

The first point there means that it’s impossible for cats-mtl type classes
to expose their base class instances implicitly; for example `F[_]: Stateful[?[_], S]`

isn’t enough
for a `Monad[F]`

to be visible in implicit scope, despite `Stateful`

containing a `Monad`

instance as a member. The root cause here is that prioritizing implicit conversions with subtyping
explicitly can’t work with cats and cats-mtl separate, as the `Monad[F]`

instance for the type
from cats will always conflict with a derived instance.

Thus `F[_]: Stateful[?[_], S]`

, translated, becomes `F[_]: Monad: Stateful[?[_], S]`

.

For some historical info on the origins of cats-mtl, see:

https://github.com/typelevel/cats/issues/1210

https://github.com/typelevel/cats/pull/1379

https://github.com/typelevel/cats/pull/1751

## Laws

Type class laws come in a few varieties in cats-mtl: internal, external, and free.

Internal laws dictate how multiple operations inter-relate. One side of the equation can always be reduced to a single function application of an operation. These express “default” implementations that should be indistinguishable in result from the actual implementation.

External laws are laws that still need to be tested but don’t fall into the internal laws.

Free laws are (in theory) unnecessary to test, because they are implied by other laws and the types of the operations in question. There will usually be rudimentary proofs or some justification attached to make sure these aren’t just made up.