Afsal Thaj

Mar 29, 2018

6 min read

Monad Transformers in Scala — Unlocked

Let’s start with a question.

Do you think the type List is a Monad? Yes, it has got a bind (flatMap) and unit (apply) method that satisfy the requirements to be monad + it follows monad laws. The monad list can be also be termed as an effect because “given a type A, lifting it (somehow) as List adds new capabilities to it, such that, we can now aggregate over A, find the sum of A and so on and so forth. The type A never had these capabilities when it was existing by itself, until we lifted it to become a List. That was a deep dive straight away. Let’s take a breath now and say — Well, if you lift your type to a Monadic Effect, it now possess more capability. Get into a spaceship and say now YOU are capable of moving to space — Spaceship Monad!

Similarly, when we lift A to the effectOption, we say now A has another capability — A can have a defined absence — None. Now, we never want to throw new Exception to mark the absence of A.

Let’s lift the type A to another effect, scala.util.Try. Now A can be a Failure or a Success and not just A. The type A inherently has the capability to handle failure. I hope, you almost got the point now. This is more or less a conceptual understanding of what effects are, and we are naming it to be a “Monad”. Pause here and play with Monads.

Effect from a different angle

Was that effect enough?

Multiple Effects and Monad Transformers.

The only way to make that happen is Monad Transformers . We are saying we need a single effect that depicts the multiple layered effect. We use scalaz’s OptionTmonad transformer to depict the effect of returning a value under the effectOption which is in turn, under the effect \/ .

You might need to stare at it for a while. When you had another effect from thin air wrapping your actual effect, you somehow want to make sure that you still have the capability to peal off all your effects with a single operation (in this case, map)and get the value without any sort of nested for comprehensions, and that’s all MonadTransformer does! In the above example, the monad transformer OptionT converts an effect ofOption layered with \/[Throwable, ?]to one single effect OptionT that makes you feel like it is still an option and you are flatMapping over it.

Mechanism behind Monad Transformers (Skip it if you want to skip it)

For those who want to know the mechanism behind monad transformers, you can easily identify it as composing monads, which is in fact a difficult task unlike applicatives, where the shapes are preserved. If you ever tried before to implement general monad composition, then you would have found that in order to implement join for nested monads F and G, you’d have to write a type such as F[G[F[G[A]]]=> F[G[A]] and that can’t be written generally. However, if G happens to have a traverse instance, we can sequence to turn G[F[_]] into F[G[_]], leading to F[F[G[G[A]]]], which in turn allow us to join the adjacent F layers as well as the adjacent G layers using their respective Monad instances. However take this with a grain of salt. It is not always true, a Traverse of G will help compose monads always.

The issue of composing monads is often addressed with a custom-written version of each monad that’s specifically constructed for composition. This kind of thing is called a monad transformer. The OptionT transformer mentioned above composes Option with any other Monad having a traverse instance.

Also its a general rule of thumb to identify layers as early as possible and get into Transformer as early as possible, and compose your executions without getting bloated. Quite often, we might end make things complicated with transformers and other monad abstractions such as Kleisli making the code a bit difficult to read, but its a team’s choice. Something like this:

My personal approach towards this problem, is to try and avoid the need of composing Monads if possible, because we have some smart monads available in libraries such as scalaz, cats and so forth that allow you to stay away from being complicated. Example, a Future that can return a disjunction (multiple layers) can be replaced with a single layer of scalaz.Task. If we think, we need to stack multiple effects then make sure you are getting into the monad transformer context as early as possible instead of pealing off abruptly certain part of the context to satisfy compiler. Anyways, we will discuss more on it soon.

Hope you enjoyed the read. Have fun!