From The Archives: Cleaner Callbacks With Partial Application

When I redesigned this site a couple of months back a really wanted to start with a clean slate so I purposefully didn’t import old posts from either the old danwebb.net or The Web’s Bollocks but while looking at the old stuff I came across this article which I believe is one of my better ones and is still as relevant as it was when I wrote it 4 or 5 months back so I decided to republish it here in case it might hit a few new eyes or someone might find it useful.

Over the past few months I’ve more and more realised that JavaScript, in many aspects, is a better functional language than an Object Orientated one and techniques like the one detailed in this article could really make JavaScript library APIs more concise and usable while retaining flexibility. Anyway, in case you missed it the first time, here it is.

I’ll come up with some new content soon, promise.

JavaScript has a lot of weaknesses, it’s OO is difficult to work with, some of it’s comparisons are broken, it’s short on built-in methods, the list goes on. One thing it has got, which is really very good, are high order functions and closures but these are still widely misunderstood and under used in JavaScript. A fair amount of information has been published on the functional aspects of JavaScript but, initially, sometimes it’s hard to imagine practical applications of these techniques even if you do get your head around them.

In this article I’m going to tackle one aspect of JavaScript functional programming, partial application and how you can use it to create reusable callback functions for event handlers, XMLHttpRequest or anything that takes a function as an argument. Partial application is creating a function that returns another function where some of the arguments are pre-filled for you.

Here’s a really simple example:

function adder(num) {
  return function(a) {
    return a + num;
  }
}

This function returns functions that add the given number to the argument:

var plus5 = adder(5);
plus5(7); //=> 12
plus5(1); //=> 6

This works because the returned function remembers the value of num originally passed into adder(). This is behaviour is called a closure but you can read more about them elsewhere. Let’s get on to how you can apply this technique.

function map(arr, iterator) {
  var narr = [];
  for (var i = 0; i < arr.length; i++) narr.push(iterator(arr[i], i));
  return narr;
}

This function is very simple version of the handy map method as implemented in many scripting languages like Python and Ruby. Essentially, it takes an array and a function then loops through the array passing each value in the array to the iterator function. It then returns a new array which contains the returned value of each function call.

var nums = map(["1", "2", "3"], parseInt); //=> [1, 2, 3]

function getElement(id) {
  return document.getElementById(id);
}

var els = map(['a-div', 'a-form'], getElement);  //=> returns an array of DOM Nodes

Now say you wanted to call a method on each of these such as toUpperCase(). You could do this:

var caps = map(['a', 'b', 'c'], function(letter) {
    return letter.toUpperCase();
});

But what if you find yourself wanting to call lots of methods on objects in map(). You can generalise with a partially applied function:

function callMethod(method) {
  return function(obj) {
    return obj[method]();
  }
}

This returns a function that will call the given method on any object you pass it:

var upperCase = callMethod('toUpperCase');
upperCase('a'); //=> returns 'A'

Now you can use this for your function in map:

map(['a', 'b', 'c'], callMethod('toUpperCase'));  //=> ['A', 'B', 'C']

How elegant is that? This is just a simple example but lets get on to how this helps us with callbacks. Say we have an ajax function called request. It’s takes a url and a callback function for when it’s loaded. We want to update an element with the response:

request('comment.php', function(resp) {  
  document.getElementById('item').innerHTML = resp.responseText;
});

We can generalise the update callback with a curried function like this:

function update(id) {
  return function(resp) {
    document.getElementById(id).innerHTML = resp.responseText;
  }
}

Now we can write our request call like this:

request('comment.php', update('item'));

Nice eh? How about if we made a whole raft of other curried functions to automate other types of response:

request('data.json', sendTo(processData));
request('form.php', updateForm('comments'));
request('time/now', insertAfter('header'))
request('thing.rjs', evaluateResponseIf('text/javascript'));

sendTo(), updateForm() and evaluateResponseIf() all are functions that return pre-built callback functions for common tasks, the implementation of which is left as an exercise for the reader (I hate it when people say that…well, back at ‘cha blogosphere!). In a large body of code creating a small amount of functions like this can make your code much more readable and maintainable.

There are tonnes of ways of using partial application in your scripts, not just for callbacks. I’d really like to see some of the libraries using techniques like this to simplify the function calls for common cases…

new Ajax.Request('entry/create', postFormAndUpdate('formname', 'mydiv'));

…would be quite nice. But of course, you can make your own.

3 Comments (Closed)

I like this. You end up with some very readable code using this technique.

beppubeppu at 04.11.06 / 06AM

Wow! Great article. Some really interesting ideas and concepts here.

Tobie LangelTobie Langel at 04.11.06 / 08AM

I guessed I missed one of your gem articles from the archives... I suppose I'll comment on it now. I'm starting to realize all the areas of JavaScript development that require Object Oriented code, vs the functional code that's required to make an application actually work. I generally like the way your code looks and overall adds the right amount of "syntactic sugar":http://en.wikipedia.org/wiki/Syntactic_sugar needed to make writing JavaScript fun... And not like that jQuery stuff which is more like heroine when everything is alphabet soup.

Dustin DiazDustin Diaz at 05.11.06 / 04AM

About This Article