Thursday, October 28, 2010

Transactional Monad for Scala

Scala is a nice programming language that allows both imperative and functional programming style. There is however no standard implementation of monads which would allow to work with outside world (like databases, i/o) in a pure functional way. Since there is no such thing, let's build it! Let's take a web shop as an example and step by step build our monad or whatever we might get along the way. Let's just stay practical and build some useful stuff.

So, how our database monad would look like? Monads are type constructors, so we start with a type constructor:

trait Transactional[A]

and this is how we would like to use it:

def findByPK(id:Long):Transactional[Product]

This type signature would indicate that we have a function that returns a Product object as a result of the database transaction. In JPA world it can be managed instance of Product entity. It's already useful on it's own, because now we can distinguish managed entities from not managed, which is a good thing. The code which requires managed entities will always use Transactional and the code which can live with detached ones will use objects directly.

How would we like to use our monad? I mean, encapsulating result of a transaction is good, but what can you do with it? I would say, anything you like. Literally:

trait Transactional[A] {
def map[B](f:A=>B):Transactional[B]
}

Look at List or Option, they all have this method implemented, even Google wouldn't be the same without such method as we know now. Our function thus could take a Product as a parameter and produce ... , whatever, say HTML:

def render(p:Product):String

We could write then:

def renderProduct(id:Long) = findByPK(id) map render

Neat and nice. Very simple, and we've got again something useful. We are still stuck with Transactional[String] but it's just because the end result of all our functions depends on the database transaction, so there is nothing to worry about yet, we are still on track.

It is good that we can combine pure functions with our Transactional thing, but how about other functions that also return Transactional? Like we've got the shopping basket rendered in the same way and want to combine it with the product HTML? In List and Option map() function has a brother which does exactly what we want:

trait Transactional[A] {
def map[B](f:A=>B):Transactional[B]
def flatMap[B](f:A=>Transactional[B]):Transactional[B]
}

So, now we can suck following function in our Transactional:

def renderShoppingBasket:Transactional[String]

and it will look like:

renderProduct(id) flatMap {_ => renderShoppingBasket}

Cool, but what happend to our product HTML? How do we plug functions which take more than 1 parameter? Say, we have this function to produce the final output:

def renderProductPage(productHTML:String, shoppingBasket:String):String

How do we plug such things in? This is where Scala for-comprehension come in place. The code looks like this:

for {
product <- findByPK(id)
productHTML <- renderProduct(product)
basketHTML <- renderShoppingBasket
} yield renderProductPage(productHTML, basketHTML)

Do not know about you, but I like this code. All this left arrows are actually translated to flatMap and map calls. To support 'if' we have to add also filter(p:A=>Boolean):Transactional[A]. Not big deal, but handy. We can read this arrows as extraction of object (managed entities?) out of Transactional. Keep in mind that the result will (and should!) always be another Transactional.

Our page is ready, but we still have Transactional[String], not the real String. How do we escape from it? We could simply add a get method which gives us this String, but then it will look more like a JavaBean, not a monad. Monad's are different. They abstract side-effects from pure functions. So, to get out from the Transactional, there must be a ... transaction which has to be commited to give us the outcome. Ok, here you are:

trait Transactional[A] {
def map[B](f:A=>B):Transactional[B]
def flatMap[B](f:A=>Transactional[B]):Transactional[B]
def commit:A // commit transaction and give us outcome
}

What about implementation? Let's start from the beginning, how would our findByPK function look like? How about this (JPA style) :

def findByPK(id:Long) = transactional {em:EntityManager => em.find(id, Product.class) }

I cannot think about simpler function. Let's make it compilable with this implementation:

object Transactional {
def transactional[A](body: EntityManager => A) = new Transactional {
def atomic = body
}
}

If you add import Transactional._ in your Scala code, the example above will compile. But wait, we just made very important design decision as to me. We do not contain the transaction outcome anymore, but a function that takes EntityManager as an argument and gives us the result of some operation on it. So, one more time, just because this is important: Transactional[A] contains an EntityManager => A function. It has following consequences:
  • We do not need an EntityManager instance when we construct Transactional object. Good, no dependency injection, no ThreadLocal anymore
  • Our Transactional becomes lazy. It doesn't do anything until we really need it. Cool, will see how it fly.
  • Whole transaction start/commit/rollback can be placed together, in 1 function. Great! no transaction/connection leaks!

Finally we can implement map and flatMap buddies to do our magic:

trait Transactional[A] {
def atomic:EntityManager => A
def map[B](f: A => B):Transactional[B] = transactional[B](f compose atomic)
def flatMap[B](f: A => Transactional[B]):Transactional[B] =
transactional {r => f(atomic(r)).atomic(r)}
}

Back to the commit function now. It doesn't really only commit our transaction, but executes it as a whole. Let's rename it to exec. It needs an instance of EntityManager to get the job done. Let's inject it as implicit parameter. Might get life easier. I omit implementation here, it is quite trivial and not in functional style. Yes, all side-effects happen there, so no need use functional style. At least, I cannot think of anything functionally-stylish and useful at the moment.

Well, it seems like we are done with our Transactional for now. If you ask me if we've build a real Monad the answer will be "not yet". We need to factor EntityManager out as a type parameter and add nonTransactional constructor that accepts 1 parameter of any type.

Notice that we didn't used any dependency injection, interceptors, ThreadLocals, there is no place for it anymore. In return we've got a way to combine our transactional code, distill pure functions from it and still be expressive and practical.

2 comments:

  1. I know you published this some time ago, but I was wondering if you'd developed this Transactional monad any further since then? I'm currently trying to put together a similar monad to use with Slick. This post has given me some great pointers to get started.

    ReplyDelete
  2. Would very much like a follow up to this!

    ReplyDelete