Thursday, November 25, 2010

JavaScript: Curry Does The Work

curry is a common higher order function which isn't part of JavaScript on most browsers. However Prototype, Underscore, and many other libraries do offer it. The most up to date JavaScript implementations offer the bind function which can be used for the same job. I'm not going to show you how to implement curry nor attempt a generalised lesson, this is about how curry helps me with a refactoring.

In my previous post there was some really clear redundancy in the two wrapper maker functions. Lets make try to make that go away:
 
function wrapper_maker(before, after) {
return function(wrapped) {
before();
wrapped.apply(null, arguments);
after();
}
}

Which is great for the context push wrapper:

var contextualised = wrapper_maker(context_push, context_pop);

But how are we going to get those arguments for logging functions in place? The function curry does the work, (I'll use the Prototype version, it's pretty typical).

Basically curry makes a new function that behaves as if the arguments given have been pushed in front of other arguments so the following two bits of code do the same thing:

logger('abc');

var loggerX = logger.curry('abc');
loggerX();

This can be powerful when you want to pass a function that is generalised with parameters to another function that expects a function without parameters, which is what we need:

var logged = wrapper_maker(logger.curry('start'), logger.curry('done'));

And just like that curry gives a simple solution. It works when we want to add parameters to callbacks but don't control the code that will call our callback.

Refactoring to make our wrappers with wrapper_maker means we can add functionality to all of them without the duplication we would have needed in the previous post. Here's a refactoring that makes wrappers that pass back the wrapped function's return value:
 
function wrapper_maker(before, after) {
return function(wrapped) {
var result;
before();
result = wrapped.apply(null, arguments);
after();
return result;
}
}

No comments:

Post a Comment