Understanding Monads Sample: F# Async Monad

In the previous post I explained, at a very high level, that monads are really about function composition with side activities.

In this post, I will use an example based on F#’s Async monad to demonstrate practical uses of monads.

Imagine that there are 3 asynchronous web service calls that we want to sequence together before doing something else. The calls have data dependencies where the data from the previous call is required by the next. The calls are asynchronous so the calling thread starts the request and then continues on. The thread will not block waiting for a response as is the case with regular synchronous calls. However the next service call cannot start unless the previous one is done, which requires special handling.

There are several ways of handling asynchronous service calls; two common ways are the use of Futures and callbacks. Let’s briefly review how these work:

Asynchronous
Handling Mechanism
Description
Future A call to an asynchronous service returns   immediately with a Future object. The Future will eventually contain the response from the call. The calling thread can poll the Future and proceed further when the response is available.
Callback A call to an asynchronous service is made with an extra parameter – a callback method. The way a callback is supplied is implementation specific; it can be a lambda expression; a method name; an event handler; or a ‘closure’ in the form of an inline interface implementation (such as Runnable in Java).The calling thread usually completes without waiting for a response. When the response arrives, the callback method is invoked and further processing is handled in the callback, typically on a different thread.

Depending on the need/constraints, either method may be used.
In general, the use of Futures involves wasteful polling and should be avoided,
if possible.

Lets consider the synchronous case for comparison; the main thread simply makes the three calls one after the other (all calls run over the main thread). In the Async / Future case, all calls are still run over the main thread but there is a poll-wait step after each async call. For Async / Callback, each service call takes a callback method as a parameter. After making the call, the caller thread ends. A different thread executes the callback when the
response is received; this thread then makes the next service call.

In the asynchronous cases, the programming burden is higher as extra code is needed. Service calls are not composable and code is cluttered with extra housekeeping steps. Specifically, in the Async / Callback case, the callbacks have to be nested which makes the code hard to read. To clarify, the callback method for “service 1” will contain not only the call to “service 2” but also define the callback for “service 2”; which in turn will contain the call to “service 3” and define the callback for “service 3”. Thus the callbacks definitions (if defined as in-lined Runnable implementations or lambda expressions) are nested; the callback to “service 1” will contain the callback to “service 2” which in turn will contain the callback to “service 3”. This nesting of callbacks makes the code harder to read and write.

After that rather long preamble to introduce asynchronous call handing, let’s turn our attention to F#’s Async monad. First look at how a composed synchronous call to the 3 services will look like in F#:

//service call implementations
let service1 data1 = … //returns data2
let service2 data2 = … //returns data3
let service3 data3 = … //returns data4
 
//call the services synchronously
let data1 = x
let data4 = data1 |> (service1 >> service2 >> service3)
 

Now the analogous asynchronous implementation:

 //async service calls
let service1 data1 = async{…}
let service2 data2 = async{…}
let service3 data3 = async{…}
 
//async composed call
let data1 = x
let data4 =
async { 
   let! data2 = service1 data1
   let! data3 = service1 data2
   return! service3 data3
   }
   |> Async.Parallel

Note the use of the async{…} syntax above. It is the special F# syntax for constructing monad instances by using the referenced monad implementation. The term “async” refers to the actual Async monad implementation that comes built-in with F#. The curly braces “{…}” contain code that gets wrapped up in the monad instance (recall that a monad is a kind of a wrapper).

Monad instances can be composed together via the let! (pronounced “let bang”) keyword (and related keywords). Note that functions service1, service2 and service3 themselves return async monad instances and these are being composed together in the final monad instance. (The keyword return! Is equivalent to let! except that it returns the unwrapped value when the monad is run.)

This takes us to ‘running’ the monad. A monad once created (usually) does not do anything. It is just a composition of functions waiting to be applied or run. In fact, if the special syntax is ‘de-sugared’ we would see the composed functions as a set of nested function calls (much like in the Async / Callback pattern). The special syntactic sugar makes these nested calls appear as linear calls, considerably enhancing readability. F# also uses the term “computation expression” to denote the function composition aspect of
monads.

The code sample above shows the monad instance being piped into Async.Start
function, which starts the ball rolling. The F# Async monad implementation provides
several utility functions, some of which are described below:

Async
Monad Utility Function
Description
Async.Start Launch the monad on a background thread (the current thread does not wait for its completion)
Async.RunSynchronously Launch the monad and wait for it to complete
Async.Parallel Creates a new monad instance from a collection of other   monad instances. When the new monad is run (e.g. via Async.Start) it runs the contained monads in parallel and completes only when all the nested monads  are done.

Getting back to the usefulness of monads, the code samples above demonstrate how, with the help of the right monad implementation, the code can be made much simpler.

Compare the synchronous and the asynchronous versions of the code above. The asynchronous version is somewhat more involved than the synchronous version, but not by much. The monadic, asynchronous code is straight forward and very readable. Aside from the async / curly braces syntax no other housekeeping code is needed – such as for managing threads, callbacks, synchronization, poll-wait, etc. By contrast non-monadic asynchronous code – as alluded to by the Async / Future and Async / Callback patterns – will have such housekeeping code and consequently will be more cluttered and less intention revealing.

The F# Async monad implementation allows for asynchronous computation code to be written at a higher level of abstraction. The lower level plumbing details are managed by the monad implementation.

The features used to implement Async monad are also available to F# users so anyone can implement custom monads for their own specific needs.

Advertisements

One thought on “Understanding Monads Sample: F# Async Monad

  1. Pingback: Understanding Monads | Faisal's space

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