Pages

[JS] Currying with Javascript

I first read about the term curry in the book "Javascript: The Good Parts" by Douglas Crockford. According to the author, currying allows us to create a new function by combining a function and some arguments. The example in the book was something along the following line.


function addBase(base) {
  return function(num) {
    return num + base;
  };
}

var add2 = addBase(2);
add2(6); // => 8

When I first read about it, I thought it was a cool thing to show off with your friends about your coding skills. Nevertherless, I didn't find currying to be very useful. But after a few times seeing it being used in different repos, I just realize how powerful it is as a tool to generate function and simplify callback.

But before moving on, let's see how curry works by study the simple example above one more time.

How curry works?

So, what does addBase() do? It creates and returns an anonymous function. When you call var add2 = addBase(2), you get back that anonymous functin; so add2 is now a function that will take one argument. How about base and num? Which argument are you using when you call add2(6)? Since we have said add2 is the anonymous function, we are calling it with num = 6. But where is base then?

That's the magic of javascript and closure. We have set base = 2 when we called add2 = addBase(2). Thanks to closure, the anonymous function, and so does add2, still have access to this variable, base, even after addBase have been executed. That why when you call add2(6), you get back 6 + 2 = 8.

Aha, so the idea is to create a new function with some argument being fixed, base in our example. But why don't we just write a function with that fixed argument? The thing is we only want to fix it when execute the new function. However, we want to be able to change the fixed argument to generate new functions as well. For example, you want to create add3, add4, .... You name it.

Still not impressed? Let's look at some real world examples.

Logging with namespace

Let's say instead of using console.log to print out message, you want to have a logger tool that can display the different types of messages differently, e.g. info, warn, error. For simplicity, we just prepend the message with their type. It can be done as following.


function logger(namespace) {
  return function() {
    console.log.apply(console, 
                     [namespace].concat(_.toArray(arguments)
    );
  };
}

var info = logger('INFO:'),
    warn = logger('WARN:'),
    debug = logger('DEBUG:');

info('This is an info message'); // INFO: this is an info message
warn('This is a warn message');  // INFO: this is a warn message
debug('This is a debug message');// INFO: this is a debug message

Clean callback

Let's say you want to iterate through an array of DOM elements get a certain attribute. So, the elements are changing, but the attribute we want to retrieve is fixed. We can do it this way.


function getAttribute(name) {
  return function(element) {
    return element.attribs[name];
  };
}

var links = _.map($('a'), getAttribue('href'));

Pretty neat, ha? Once you get used with curry, you will see it's very powerful and can be used very often.

No comments:

Post a Comment