If you think all this abstract babble about categories, functors, and monads has no real-world application, think again. jQuery, the popular JavaScript library that provides an enhanced interface for working with HTML is, in-fact, a monadic library.
The jQuery
object is a monad and its methods are functors. Really, they're a special type of functor called endofunctors. Endofunctors are functors that return the same category as the input, that is, F :: X -> X
. Each jQuery
method takes a jQuery
object and returns a jQuery
object, which allows methods to be chained, and they will have the type signature jFunc :: jquery-obj -> jquery-obj
.
$('li').add('p.me-too').css('color', 'red').attr({id:'foo'});
This is also what empowers jQuery's plugin framework. If the plugin takes a jQuery
object as input and returns one as output, then it can be inserted into the chain.
Let's look at how jQuery was able to implement this.
Monads are the containers that the functors "reach into" to get the data. In this way, the data can be protected and controlled by the library. jQuery provides access to the underlying data, a wrapped set of HTML elements, via its many methods.
The jQuery
object itself is written as the result of an anonymous function call.
var jQuery = (function () { var j = function (selector, context) { var jq-obj = new j.fn.init(selector, context); return jq-obj; }; j.fn = j.prototype = { init: function (selector, context) { if (!selector) { return this; } } }; j.fn.init.prototype = j.fn; return j; })();
In this highly simplified version of jQuery, it returns a function that defines the j
object, which is actually just an enhanced init
constructor.
var $ = jQuery(); // the function is returned and assigned to `$` var x = $('#select-me'); // jQuery object is returned
In the same way that functors lift values out of a container, jQuery wraps the HTML elements and provides access to them as opposed to modifying the HTML elements directly.
jQuery doesn't advertise this often, but it has its own map()
method for lifting the HTML element objects out of the wrapper. Just like the fmap()
method, the elements are lifted, something is done with them, and then they're placed back into the container. This is how many of jQuery's commands work in the backend.
$('li').map(function(index, element) { // do something to the element return element });
Another library for working with HTML elements, Prototype, does not work like this. Prototype alters the HTML elements directly via helpers. Consequently, it has not faired as well in the JavaScript community.