Scalaz Monad Transformers

Whilst gaining a deeper understanding of functional programming concepts and patterns I have found myself delving more deeply into the world of scalaz. Today the agenda is monad transformers, after some initial reading I very quickly started to see patterns in our codebase which could immediately benefit from their application.

What is a monad transformer? My definition (as a Software Engineer) is… a monad transformer is a typeclass that abstracts a monad which wraps another monad. This may not sound like something that happens a lot but it’s surprisingly common, take Future for example, all these cases below are monads nested within a monad.

Future[Option[T]]
Future[List[T]]
Future[scalaz.\/[A, B]]

Lets take the following scenario, you have two Lists of integers, and you want to add every element in List A to every element in List B and get a final List C. However both lists are wrapped in a Future. In pure Scala this may look something like this:

@ val x = Future.successful(List(1, 2, 3))
x: Future[List[Int]] = scala.concurrent.impl.Promise$KeptPromise@5da72d46
@ val y = Future.successful(List(4, 5, 6))
y: Future[List[Int]] = scala.concurrent.impl.Promise$KeptPromise@2896f3c5
@
@ x.flatMap { listA
y.map { listB =>
listA.flatMap { i =>
listB.map { j =>
i + j
}
}
}
}
res17: Future[List[Int]] = Success(List(5, 6, 7, 6, 7, 8, 7, 8, 9))

Or alternatively using the syntactic sugar of a for comprehension:

@ for {
listA <- x
listB <- y
} yield {
for {
i <- listA
j <- listB
} yield i + j
}
res18: Future[List[Int]] = Success(List(5, 6, 7, 6, 7, 8, 7, 8, 9))

Notice that there is no way of accessing the underlying data without having to map over the Future followed by the List, which leads to the nested code you see above, this situation is where monad transformers can help us.

In our example our top level type was a Future[List[Int]], when choosing which monad transformer to use, you always choose the inner most type, in this case List[Int] is our inner most type so we will use the ListT monad transformer. The ListT apply function is as follows:

def apply[A](a: M[List[A]]) = new ListT[M, A](a)

Therefore we can use this to convert our Future[List[Int]] to the ListT monad type which in this case will be a ListT[Future, Int]. We can now write our addition in terms of the new monad type which has abstracted the mapping of the Future:

@ for {
i <- ListT(x)
j <- ListT(y)
} yield i + j
res20: ListT[Future, Int] = ListT(Success(List(5, 6, 7, 6, 7, 8, 7, 8, 9)))

Notice we returned ListT[Future, Int], as with any Functor, calling map will always return the same monad type wrapping the transformed value. This allows you to chain/compose operations in terms of your monad transformers until you are ready to unwrap back to your original type, which can be done using the run method:

@ res20.run
res21: Future[List[Int]] = Success(List(5, 6, 7, 6, 7, 8, 7, 8, 9))

In summary monad transformers give you a powerful abstraction to work on the underlying data of a monadic type when it itself is wrapped in a monad. It reduces code complexity and enhances readability by abstracting the wiring of drilling down into the nested datatypes. ScalaZ provides implementations of monad transformers for many types including EitherT, ListT, OptionT and ReaderT to name a few.

Tagged with: