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 and not a Mathematician with a PhD in Category Theory is… a monad transformer is a monadic type 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.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s