Monad Transformers aren’t hard!
For some people monad transformers are magic, today we will learn what is monad transformer, why would one use it and why there are some monad transformers, but not the others.
Let’s assume that we are implementing a function to find when a user created his facebook account using the following API:
We will be making asynchronous http calls, which might not return any result (if user doesn’t exist or it doesn’t have facebook account) that is why return type is Future[Option[T]].
From the first sight solution might seem to be easy:
But it’s not correct. Because for-comprehension in scala can go only 1 layer deep. Correct solution would look a bit cumbersome:
Return type of Future[Option[T]] covers multiple scenarios:
- asynchronous request succeeded (future is successfully completed) and user/account exists
- asynchronous request succeeded but user/account doesn’t exist
- asynchronous request failed (e.g. timeout error)
While we care only about the first case, others can be interpreted as failures.
What if Future[Option[T]] was a monad.
Maybe wrap Future[Option[T]] into FutureOption[T], add
flatMap on top of it:
So now FutureOption[T] can be used instead of Future[Option[T]]:
keep in mind that createdDate is of type FutureOption[LocalDateTime], it can be converted to Future[Option[LocalDateTime]] by calling futureOption on createdDate.
The same pattern can be used for combining List[Option[T]]:
ListOption looks very similar to FutureOption, except line 10:
List(None) or Future.successful(None). List and Future both are Monads, lets abstract over some Monad — F[_](line 10 would look like
m.pure(None)) and implement OptionT[F[_], A]:
Meet your the first monad transformer — OptionT.
The same approach can be used to implement EitherT:
But is it possible to come up with something different from OptionT and EitherT?
And it’s a good question, ListT is a bit tricky, but general idea is the same:
We implemented ListT monad transformer, but it appeared that it’s not a monad, ListT doesn’t obey associativity law, here is a gist, so officially it’s not a monad transformer.
And the last monad transformer for today will be FutureT:
FutureT is there, so as 1 caveat. FutureT is blocking a thread of execution, because
futureFFuture: Future[F[Future[B]]] can’t easily be converted to
F[Future[Future[B]]]. And I guess this is one of the reasons we don’t see anyone using it.
But what is monad transformer, monad transformer is a monad that extends a behaviour of underlying monad with some other monad F and it removes boilerplate of composing nested monads (too many m-words in 1 sentence).
To recap, today we crafted couple of monad transformers like OptionT, EitherT, got to know that ListT is not a monad, covered that FutureT cannot be implemented without blocking.
P.S. technically Try isn’t a monad, thus no monad transformer for it.