Let’s Put jQuery in a Monad

Exploring the IO type in Javascript

Drew Tipson
8 min readMar 19, 2016

A while back, I built a little proof (mostly to myself) of concept called thenUse. The idea was that jQuery DOM methods, especially when chained, are always called immediately, and with that sequence of methods already bound to a specific jQuery object. I wanted a way to abstract the method chains from the object, and thus do these sorts of wacky things instead:

If you don’t know what a “thunk” is, it’s a just a function that wraps some pre-configured side-effect/behavior that you can call as you please later on.

Using this little toolkit, I could first build up chains of jQuery methods and later on decide what elements they’d be used on. Or I could write callbacks for event handlers/Deferreds that could either ingest the element they were called on or fire off pre-defined behavior without further worry about the context of “this.”

The internals of the library itself were almost unintelligible (and probably very poorly executed), but I still figured that the actual result and its approach was sort of interesting. Using it sometimes required less code, a bit less repetition, and I even used it on a project or two.

Now I’m older and wiser. And I think what I was trying to get at was separating the act of chaining together computations from their execution.

Well, as it turns out, there’s a Monad for that (and don’t worry if you still don’t know what a Monad is). It’s usually called IO. And it’s deviously simple:

Here we have an on-the-cheap constructor that just wraps whatever function it’s passed by storing it in a curiously named “runUnsafe” property.

Then we have our “pointed” interface: a way to lift something “up” into the IO type. If given a value, IO.of creates a function that will just return that value. If given a function, it “thunks” it: that is, adds an extra layer of execution that will be necessary before it’s actually called.

Before going any further, lets just see what this does for us.

IO.of(5);//-> IO[ _→ 5 ]

Hunh? What on earth is the point (heh) of that? Now that we have a 5 wrapped in IO… we’ve basically just made it a 5 that’s extra annoying to get at or do anything with. Because now the only way we can get the 5 back out of the IO type is to do this:

IO.of(5).runUnsafe();//-> 5

Here’s the thing though: it’s an incredibly annoying to get that 5 back out on purpose. The ultimate goal of IO is to maintain the purity of functional computations for as long as absolutely necessary: right up until they’re actually meant to be executed. And that means not actually interacting with anything mutable or ephemeral until it’s explicitly time to do so. It’s a structured of specifying ahead of time what we will be interacting with.

If you think about it, that’s not actually all that crazy. You do it all the time! Consider just about any callback function, like, say the kind you’d pass to jQuery’s $(document).ready method. The logic you put inside that callback is meant to be postponed until the DOM elements you want to work on actually exist on the page in the first place. Of course, that .ready method callback often then just contains a forest of imperatively described side-effects… the very antithesis of functional programming.

IO serves a similar purpose, but in a very different, more structures way: it allows you to compose together functions that will have side effects together without actually causing those side effects. That is, instead of cramming them all into one big protective callback enclosure, it provides the interfaces necessary to describe what they should do without actually doing them.

To get started, let’s steal our convenience document.getElementsByTagName wrapper from the Array Monad article:

var $getByTag = tag => Array.from(document.getElementsByTagName(tag));

There’s something inherently dangerous about just running this operation any-old where and when. The main danger is this: depending on the state of the DOM, it might return all sorts of different things every time we call it. If we call $getByTag(‘body’) in the <head/> of a webpage, for instance, it’s going to return an empty Array! If we call it in the body, it’ll return one element: the body itself. If some absolute madman happens to run this at some random point: $(‘body’).after($(‘<body/>’)); then it’ll return TWO elements, two bodies! Not to mention: does the document object itself even exist at all when it’s called? (ok, probably, but stilllll…)

So there’s no question here: $getByTag is not a pure function. Which is to say, it’s a highly unpredictable function. The same input will NOT always return the same output. One dead giveaway is that phrase “the state of the DOM.” State. By now, almost all modern javascript developers know that word means big trouble.

So how can we sequence computations that involve interacting with the Lovecraftian horror that is the mutable DOM? Using the IO Monad is exactly one such pattern.

var $getByTagIO = tag => IO(_=>$getByTag(tag));
$getByTagIO(‘body’);
//-> IO[ _ → $getByTag(‘body’) ]

This $getByTagIO thing we’ve created is a sort of skyhook dangling $getbyTag and ‘body’ out in space, but with each bit kept a safe distance away from each other, lest they actually combine and do something prematurely. Only by very deliberately running $getByTagIO(‘body’).runUnsafe() do we ever actually hook them together and get a result.

This might still seem a little silly: after all, we still have to call this thing when we need to use it, and if we know when we want to call it, why not just use $getByTag directly? Your objections are noted. But then, we haven’t actually implemented the rest of IO yet!

Ahhh, much better. Let’s wet our beaks with some profoundly useless but simple to understand examples:

IO.of(5).map(x=>x+1);//→ IO[ _ → 5+1]
IO.of(5).chain(x=>IO.of(x+1));//→ IO[ _ → 5+1]

Being able to elevate a simple value into a Monadic wrapper is always convenient just in case, but IO is really meant for encapsulating functions. And, in particular, impure functions that may have unpredictable side-effects. So let’s look at how that works:

It might seem a little extreme to treat console.log as a dangerous side effect, but we might as well get used to it. It still counts, and it’s simple enough for honest examples.

Our IO skyhooks are able to suspend sections of road that form a sort of bridge from point A to result B to result C, all still dangling out in midair. And when we’re ready to drive something over it, runUnsafe links them all together and hits the gas pedal.

Terrible metaphors aside, the point is that we now have ways to chain further functionality off of our $getByTagIO IO result… without actually running anything. That’s the point, and that is at least interesting (we’ll debate whether or not it’s valuable at the end).

Let’s see how this will work:

Here, $outputIO is just a helper that allows us to create a specific function of the type IO.chain expects: a function that takes the innerValue returned from the previous IO and transforms it into a new IO that wraps the operation we intend to have happen.

If all of this seems a little overwrought, you’re not wrong. IO is an absolute necessity in a pure functional language like Haskell: without it, programs cannot compile and functions cannot interact with the outside world (i.e. “reality”: sometimes jokingly referred to as the “Ambient” Monad). In a language like Javascript where we’re used to being able to do anything anywhere at any time, it feels like tying both our hands behind our backs and maybe a leg as well. And all these extra constructs have a performance cost to boot.

Maybe that cost is justified, maybe it’s not. What they buy us in Javascript is more consistent and composable Monadic interfaces as well as an extra layer of safety between our instructions and their execution. But maybe you think you’re already careful enough… and in many cases, more complex Monads like State or Future/Task or even Streams are better abstractions for separating our computation compositions from actual execution. Nevertheless, exploring IO is relatively simple and instructive. Let’s continue, now that we have a general sense of it.

And in that spirit, let’s turn our attention to jQuery. You’ll hear periodically that parts of jQuery are already Monadic, and there’s some whiff of truth to that. Consider the operation $(‘div’).children(); The selector function $(‘div’) returns a special jQuery object called a collection, which is something like an Array, but actually represented as an object with numerical indexes and an artificial length property (making it an “Array-like”). The collection here would contain all the <div/> DOMnodes on the current page inside a special container.

Calling a method like .children(), then, does something like flatMapping (just with a particular pre-baked operation) in that the result is not a collection of collections, but rather just a new collection of every childNode of every <div/>.

But we’re in weird waters on this. For instance, $(‘div’) is not really a Functor, despite having a .map operation: its .map method has the wrong signature ( index, DOMelement) and really intends you to use “this” upfront to deal with mapped elements. And of course jQuery is riddled with mutation, impurity, and side-effects: that’s it purpose, after all: to make it short-and-sweet-and-cross-browser-safe to access and mutate the DOM. There’s nothing wrong with all this in and of itself: jQuery is very good at what it does (though it seems like React is finally pushing the FE javascript community in another direction), it makes things very easy. But in the paradigm of FP, there’s such a thing as… too easy. Dangerous.

If we wish, is it possible to do something about that, without totally re-writing jQuery (to, literally, make it harder to use)? Certainly. We can wrap it up in IO!

This will involve a slightly longer gist than normal, but hopefully it’s straightforward enough to read through top to bottom (you do do that with code, right? Read it like a book, and digest each expression, instead of skimming? You really should!)

We start out by implementing the basics of the IO monad, then build on some methods for automatically wrapping calls to jQuery’s basic element selector method in the IO type. Finally, we add some methods that make it easier to chain jQuery methods without actually calling them, or necessarily even specifying upfront what element we’ll be executing them on.

Here’s a fiddle demonstrating some basic usage, of the more complex event handler cases. I didn’t try to make the example particularly pretty, but that’s the basic feel. With this, we’ve now married the IO Monad pattern with jQuery and essentially reproduced most of the thunky functionality of my kludgy thenUse.

To break it down a bit: a command like $IO(‘body’); would return an IO that, when run, will return a jQuery collection with the <body/> node in it. $IO(‘body’).$(‘fadeOut’) would return an IO that, when .runUnsafe(), would find the <body/> and then apply the jQuery method .fadeOut() to it. That’s essentially like creating a thunk that will run that effect: var fadeOutBody = _ => $(‘body’).fadeOut(); but a lot more chainable (i.e. you can add on more methods later, extract data, anything you can do with jQuery).

For the special event handler functions, all I’m doing is making it possible to hook up an existing chain of IO behavior with a jQuery event handler. $useIO would return an event handler function that would call a specific IO chain when that event executes. $useSelfOnIO is the same idea, except that it creates an IO wrapper for the jQuery collection that the event returns, and then chains an IO behaviors onto that collection (that’s why it expects a “headless” IO, which is just IO(x=>x) so that there’s something to chain off of).

Obviously, this isn’t a pattern most people are going to use with jQuery: we’ve all mostly gotten along just fine with crazily impure, non-composable DOM mutation. And more modern, alternative approaches like React avoid mutating the DOM, opting instead to render it from state (which is a bit more functional, though not taking it as far as Elm).

But still: try this approach out, let me know what you think, and see if it’s helps you understand IO and Monads a little better than before.

As we all move towards pushing mutation and IO operations out to the exposed edges of our applications (instead of leaving surprise side-effects and state mutations all over the dang place), it’s a pattern worth thinking through even if it’s not ultimately your skyhook of choice.

Update: here’s a much more comprehensive version, with step-by-step explanation, and ditching jQuery entirely for document.querySelectorAll just to demonstrate how simple some of this stuff is:

https://gist.github.com/dtipson/14c033229d6ebc94bc612776423150e4

--

--

Drew Tipson

Primarily Javascript, potentially personal, possibly pointless. I welcome and am fascinated by your many marvelous opinions.