More playing with monads

This blog post will look at using the continuation monad to sum the nodes in a tree, and then look at a case where a more complex monad is needed.

The book Real World Functional Programming gave an example of an unbalanced tree, which was deeply nested such that recursing over the tree to sum the values would blow the stack:

/blog/2012-06-27-more-playing-with-monads/images/clip_image001_thumb25255B325255D-300x132.png

/blog/2012-06-27-more-playing-with-monads/images/clip_image00225255B425255D-300x104.png

The solution provided was to sum the tree using continuation passing:

/blog/2012-06-27-more-playing-with-monads/images/clip_image00325255B425255D-300x76.png

This is now able to sum the tree without crashing, but the code doesn’t flow as well as in the original sumTree method. However, monads can come to the rescue again, in this case the continuation monad:

/blog/2012-06-27-more-playing-with-monads/images/clip_image00425255B425255D-300x156.png

The code flow now is easier to understand. The continuation passing style and continuation monad turns the code flow inside out, which isn’t a big deal, but means that the func to perform on the result needs to be passed in:

/blog/2012-06-27-more-playing-with-monads/images/clip_image00525255B425255D-300x26.png

Parallel sum

This is all well and good, but to exploit multicore machines, I want to take advantage of the parallelism in the tree summation algorithm, by summing the different branches of the nodes using tasks. A naïve implementation to do this may be:

/blog/2012-06-27-more-playing-with-monads/images/clip_image00625255B425255D-300x93.png

 

The problem with this is that it runs slower than the sequential case for a large balanced tree –if there are only 4 cores in the system it’s counter-productive to create thousands of tasks. The current depth of the tree should be passed around, and tasks spawned until traversal reaches that depth:

/blog/2012-06-27-more-playing-with-monads/images/clip_image007_thumb25255B225255D-300x123.png

 

 

This is now faster for a large balanced tree, but for the deeply nested unbalanced tree it throws a StackOverflowException. Note that sumTreeParallel coped with arbitrarily nested trees, as the work to be done no longer kept being pushed onto the stack.

More state is being passed around (the current depth), which is external to the main business logic – it does feel like the state monad could be used here. First though, this is fixed up so that it has the efficiency of the parallel summation, but can cope with arbitrarily deeply nested trees:

/blog/2012-06-27-more-playing-with-monads/images/clip_image00825255B425255D-297x300.png

Urghhhh. The code is definitely more convoluted now – I played with using the state monad to pass the depth around, but it’s not tail recursive. I thought that it might be possible to make use of the continuation monad here, but the problem is, is that it really needs aspects of both the state and continuation monads. I’m not sure how monad combinations work in F#, but I thought I’d throw this out here to see if anyone shows me the light. The post The Mother of All Monads does say that all other monads could be implemented in terms of the continuation monad, but I couldn’t see an example of anyone implementing the state monad in terms of the continuation monad.