This post is mean to explain the differences between the 3. These 3 terms come up regularly and it is important to differentiate them.

### Requirement

This post aims for the pre-intermediate haskell programmers that have already learn the fundamental concept (monad, functor ...) and have written some haskell program already.

# Monad Transformer

`monad-transformer`

is a type container such as `ReaderT`

, `WriterT`

... We will focus on the use-case only.

When people refer to `monad-transformer`

they usually mean something their `monad-transformer`

stack: `newtype AppM a = ReaderT Env (LoggingT IO) a`

The reason why haskell application use this stack over just `IO ()`

because `monad-transformer`

provide extra functionality for example `ReaderT`

provide `ask`

function to get env variable instead of passing it in all the functions.

# MTL - Monad Transformer Library

`mtl`

library define various typeclasses that help working with monad-transformer. Remember that `mtl`

is a library not a `monad-transformer`

.

The reason why mtl is created because when we define a function to work with `monad-transformer`

stack, we do not want to use `AppM`

in the function type.

For example:

- instead of this
`askEnvAndReadFile :: FilePath -> AppM String`

- we want to use this
`askEnvAndReadFile :: (MonadReader Env m, MonadIO m) -> FilePath -> m String`

MonadReader and MonadIO are typeclasses that show that the function can ask for Env and can do IO action. This function can be use within any `monad-trasnformer`

stack that has ReaderT and IO in them, not just our predefined AppM.

# MTL-style (tagless final)

Sometimes when mtl is being discussed, it is not about the mtl library itself but the techniques that mtl library uses which is the Tagless Final. I will not provide detail explanation of this, but basically this is a technique that help haskell programmer write function with business model constraint.

For example, we want to define a function that can manage certain resources (make api call to get the resources). We do not want the function to be able to do any IO action.

Below show a function that can only make http call and log, not any other action

```
app :: (ManageResource m, HasLogging m) -> m ()
app = do
result <- makeApiCall
log (show result)
pure ()
```

Notice that `ManageResource`

and `HasLogging`

is a typeclass that has nothing to do with mtl library, but instead we define this by ourself:

```
class (Monad m) => ManageResource m where
makeApiCall :: m Resource
```

This is just an interface only, the implementation `makeApiCallIO`

will have `MonadIO`

constraint or return IO monad: `makeApiCallIO :: IO Resource)`

.

So when we want to use this function with our stack AppM we can define an instance for this `ManageResource`

typeclass.

```
instance ManageResource AppM where
makeApiCall = makeApiCallIO
```

The useful thing of using typeclass constraints instead of concrete type such as `AppM`

is that we can use a mock stack MockM where we do not actually do any IO action.

Example:

```
makeApiCallMock :: m Resource
makeApiCallMock = pure someMockData
instance ManageResource MockM where
makeApiCall = makeApiCallMock
```

## Conclusion

As we can see, the 3 terms are different. The most important thing is the `mtl-style`

technique that is used a lot in realworld haskell application (HasDatabase, HasLogging ...). It is important to be able to read other people constraint typeclasses as well as create our own. I was confused to this 3 terms as well before, hope this clears up some misunderstanding.

## Top comments (0)