Javascript Schönfinkeling

In Javascript is it extremely common to pass function as parameters to other functions.

function addOne(a) {
    return a + 1;
}

[1,2,3,4].map(addOne);

This is all good. However when your function takes more than one parameter, you can’t just pass the function directly so you need to create a new function which calls the original one:

function add(a,b) {
    return a + b;
}

[1,2,3,4].map(function(a) {
    return add(a,1);
});

This does work, but you do lose some expressiveness by doing so - the semantics of the code gets lost in the syntax.

We can break up our add function to return a nested series of functions - one for each parameter:

function add(a) {
    return function(b) {
        return a + b;
    };
}

[1,2,3,4].map(add(1));

This technique of taking a function that accepts multiple parameters and converting it into a chain of functions, each accepting one parameter, is known as Currying (also known as Schönfinkeling).

Although it is more expressive to call, it is a bit of a pain to have to write that curried function. It would be better if we could write something that could convert a standard function into a curried function.

So, instead of capturing the parameters in a series of nested closures, the following function will collect the passed parameter in an array. This is captured in a closure that is then returned for the next call in the chain. When all the required parameters have been collected, it calls the our function:

var curry = function(fn) {

  var curryOrCall = function(args) {
    if (fn.length === args.length) {
      // All parameters have been passed, call the original function
      return fn.apply(this, args);
    }
    
    return function(param) {
      // Create a copy of the arguments
      var myargs=args.slice(0); 
      // Add the new argument to the list.
      myargs.push(param); 
      return curryOrCall(myargs);
    };
  };
  
  return curryOrCall([]); 
};

Used like:

var add = curry(add(a,b) {
    return a + b;
});

[1,2,3,4].map(add(1));

This is all good and well, but we can only pass one parameter at a time, so it can get a little annoying, and inefficient, if there are several parameters you need to deal with.

var add3 = curry(add(a,b,c) {
    return a + b + c;
});

[1,2,3,4].map(add3(1)(2));

Pure currying means that each function in the chain must take only one parameter. In practical terms, often we will have multiple parameters that we want to pass, like: [1,2,3,4].map(add3(1,2));

###Partial function application

This is what partial function application is all about. Lets adjust our curry function to enable multiple parameters at a time:

var partial = function(fn) {

  var partialOrCall = function(args) {
    if (fn.length === args.length) {
      // All parameters have been passed, call the original function
      return fn.apply(this, args);
    }

    return function(/* arguments */) {
      // Add all passed arguments to our arguments list
      var myargs=args.slice(0).concat(
        Array.prototype.slice.call(arguments, 0));
      return partialOrCall(myargs);
    };
  };

  return partialOrCall([]);
}

So now it works with multiple parameters:

var add = partial(add3(a,b,c) {
    return a + b + c;
});

[1,2,3,4].map(add3(1,2));

// Note currying still works if you really want to:
[1,2,3,4].map(add3(1)(2));


###Uses

####Passing to higher-order functions Partial function application lets you be more expressive when passing functions to higher-order functions such as map.

[1,2,3,4].map(function(a) {
    return add(4, a);
});

vs

[1,2,3,4].map(add(4));

####Creating member functions

If you have an object whose member functions invoke largely the same functionality but with some slight differences, you can set these members to be a partially applied function against one common function. This is used in Angular JS:

 function supportObject(delegate) {
    return function(key, value) {
      ...
    };
  }

providerCache = {
        $provide: {
            provider: supportObject(provider),
            factory: supportObject(factory),
            service: supportObject(service),
            value: supportObject(value),
            constant: supportObject(constant)
            ...
          }
      },

(This could be rewritten with our partial method as):

var supportObject = partial(function(delegate, key, value) {
      ...
}

providerCache = {
        $provide: {
            provider: supportObject(provider),
            factory: supportObject(factory),
            service: supportObject(service),
            value: supportObject(value),
            constant: supportObject(constant)
            ...
          }
      },