How to build your own Monads

Monad

Monads are beautiful, misunderstood and useful. In this post we will explore how to build your own monads in JavaScript. We will rebuild jQuery's method chaining functionality and implement a monadic Google client for node.js.

Before we begin, I think we should address why monads are useful by looking at where they are used. Let's take some popular examples:

  • jQuery is a monad: One of the most popular JavaScript libraries is a monad! And jQuery's API is quite powerful, because of its monadic structure - a structure that we will rebuild in this post.
  • dojo.Deferred and Twisted Deferred are monads. Deferreds remove some of the complexities surrounding non-blocking code.
  • Haskell Monads: Haskell is filled with monads and they are used to handle and abstract things like state and I/O.

The three laws of monads

There are three simple laws of monads:

  • 1) Monads wraps themselves around other data types.
  • 2) Monads have an operation, called return, that performs the wrapping.
  • 3) Monads have an operation, called bind, that allows to feed the value wrapped inside the monad into another function (these functions should return a monad).

How do these laws apply to jQuery?

  • $ function wraps itself around DOM elements (satisfies law 1 and 2)
  • $('#some_div').html('test').fadeOut() == $('#some_div'). In other words a function like $(...).html returns $(...) which satisfies law 3

I won't say that much more about laws of monads, but I will link for some further reading if you are interested to know more details:

Implementing jQuery method chaining

Method chaining is quite powerful in jQuery and it allows you to do following:

$("p.neat").addClass("ohmy").show("slow").fadeIn()

We will now proceed to implement method chaining just for id selectors. Our implementation will allow following functionality:

$('some_div').
    html('This is a test').
    css('backgroundColor', '#ffffcc').
    css('color', '#999999')

It's actually quite simple to build this, here is the code:

$ = function(elm_id) {
    return new MonadIdSelector(elm_id);
}

MonadIdSelector = function(elm_id) {
    this.elm = document.getElementById(elm_id);
    return this;
}
MonadIdSelector.prototype = {

    html: function(html) {
        this.elm.innerHTML = html;
        return this;
    },

    css: function(prop, value) {
        this.elm.style[prop] = value;
        return this;
    }
    
}

The most important aspect to notice about the code is that all methods of MonadIdSelector return itself! This allows method chaining and satisfies the 3. law of monads. If you check out jQuery code you will find a similar pattern, for example let's see how $(...).empty is implemented:

empty: function() {
    for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
        // Remove element nodes and prevent memory leaks
        if ( elem.nodeType === 1 ) {
            jQuery.cleanData( elem.getElementsByTagName("*") );
        }

        // Remove any remaining nodes
        while ( elem.firstChild ) {
            elem.removeChild( elem.firstChild );
        }
    }
    
    return this;
},

Implementing a monadic Google client for node.js

In the last example we will implement a monadic client to access Google from node.js. The implementation isn't really complete since I want to showcase the core of monads (and not how to build HTTP clients in node.js ;-)). The implementation will allow us to do following:

googleClient('/').
    addCallback(function(response) {
        sys.puts('Successful request done!')
        sys.puts('HEADERS: ' + JSON.stringify(response.headers));
    }).
    addErrback(function(response) {
        sys.puts('Unsuccessful request done?')
        sys.puts('HEADERS: ' + JSON.stringify(response.headers));
    }).
    sendReq()

So basically we will build the deferred monad! The implementation is again quite simple and is very similar to MonadIdSelector:

var sys = require('sys'),
   http = require('http');

googleClient = function(path) {
    return new MonadGoogleClient(path);
}

MonadGoogleClient = function(path) {
    this.path = path
    this.callbacks = []
    this.errbacks = []
    return this
}
MonadGoogleClient.prototype = {

    addCallback: function(cb) {
        this.callbacks.push(cb)
        return this
    },

    addErrback: function(cb) {
        this.errbacks.push(cb)
        return this
    },

    sendReq: function() {
        var self = this

        var google = http.createClient(80, 'www.google.com');

        var request = google.request('GET', this.path);
        request.addListener('response', function(response) {
            var status = response.statusCode
            if(status == 302 || status == 200)
                self.callbacks.forEach(function(cb) {
                    cb(response)
                })
            else
                self.errbacks.forEach(function(cb) {
                    cb(response)
                })
        })
        request.end()

        return this
    }
    
}

If you look at the code you should notice return this. Also do check the similarities with MonadIdSelector - they basically have the same structure!

Last words!

I hope you found this useful and that you will use monads in your own code.

Happy monad hacking ;-)

26. May 2010 Code · Code improvement · Design · JavaScript · node.js · Tips
© Amir Salihefendic