How to build your own MonadsMonads 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:
The three laws of monadsThere are three simple laws of monads:
How do these laws apply to jQuery?
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 chainingMethod 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.jsIn 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
|
|