Selective partial application

Selective argument binding using lodash, ramda and heroin

Sometimes we know values for some arguments to a function. JavaScript allows applying or binding these values to the function. The result is a new function that wraps around old function supplying all arguments

1
2
3
function add(a, b) { return a + b; }
var add10 = add.bind(null, 10);
console.log(add10(4)); // 14 (a = 10, b = 4)

The Function.prototype.bind method is included in EcmaScript5 standard and is supported in almost every browser (Phantomjs v1 being notable exception). You can even bind all arguments the function expects

1
2
var add10to3 = add.bind(null, 10, 3);
console.log(add10to3()); // 13 (a = 10, b = 3)

bind applies arguments from left to right. What if you would like to apply arguments from right to left? It is easy to write a function that does this, or use a 3rd party library. For example using partialRight from lodash is one candidate

1
2
3
var _ = require('lodash');
var plus10 = _.partialRight(add, 10);
console.log(plus10('foo')); // foo10 (a = 'foo', b = 10)

Selective application

As a rule of thumb, I advise placing arguments least like to change on the left of the function signature.

In some cases though neither left nor right application works. For example, _.map method takes 3 arguments: collection, callback and this argument. Usually, the callback function can be constructed right away, but binding it to the second argument is impossible. You cannot skip arguments!

1
2
3
// does not work yet
var add10ToEveryElement = _.partial(_.map, <skip>, add10);
console.log(add10ToEveryElement([3, 5])); // [13, 15]

I call this problem selective application because I select which arguments to apply. Here is how this problem is solved by lodash, ramda and finally my little utility function called heroin.

lodash

Next major release of lodash v3 is going to have placeholders. You can skip binding arguments by passing _ itself as a special value

1
2
3
// left to right, but skip first argument
var plus10 = _.partial(add, _, 10);
console.log(plus10('foo')); // foo10

I am looking forward to this release.

spots

To avoid waiting for lodash placeholders, I wrote my own placeholder utility spots.

1
2
3
4
5
function add4(a, b, c, d) { return a + b + c + d; }
var S = require('spots');
var applyEven = S(add4, S, 1, S, 2);
applyEven(-1, -2); // 0
// equivalent to add4(-1, 1, -2, 2);

Because spots preserves the bound context (version >= 0.5.0), you can use it with methods, for example to remove boilerplate when building middleware routes

1
2
3
4
5
6
7
8
9
10
// same callbacks to check if the user is logged in and authorized
app.get('/repos', passportConf.isAuthenticated, passportConf.isAuthorized, ghController.getRepos);
app.get('/repos/:user/:name', passportConf.isAuthenticated, passportConf.isAuthorized, ghController.getRepo);
app.get('/repos/view/:user/:name', passportConf.isAuthenticated, passportConf.isAuthorized, ghController.viewFile);
// prefill 2 middle arguments using spots
var S = require('spots');
var authGet = S(app.get, S, passportConf.isAuthenticated, passportConf.isAuthorized).bind(app);
authGet('/repos', ghController.getRepos);
authGet('/repos/:user/:name', ghController.getRepo);
authGet('/repos/view/:user/:name', ghController.viewFile);

ramda

Selective application in Ramda is a difficult question because almost every function in the library is curried by default. This makes left to right application natural, but some functions are more naturally applied from the right first. These functions are called operators. For example, the division function is an operator. Normally, it divides first argument by the second one:

var R = require('ramda');
// R.divide = function(a, b) { return a / b}
console.log(R.divide(10, 2)); // 5 (a = 10, b = 2)

If we apply a single argument to R.divide, should it bind to the first position or the second? After a long discussion, the Ramda authors have decided that the application order should follow from most common use case. In this case we often divide BY a certain number (second argument), and less often divide a certain number (first argument).

var divideBy10 = R.divide(10);
console.log(divideBy10(500)); // 50 (a = 500, b = 10)

What if you really want to apply first argument? You need to pass undefined as the second!

var divide10 = R.divide(10, undefined);
console.log(divide10(5)); // 2 (a = 10, b = 5)

The same logic applies to any operator function where order of arguments matters (subtraction, comparisons). The downside is that sometimes undefined is a valid value to be applied. I would prefer using library reference ramda itself as a placeholder.

heroin

Finally, I have a tiny dependency injection utility called heroin. It applies arguments by name, and not by position or semantics.

1
2
3
4
5
6
var selective = require('heroin');
function divide(a, b) { return a / b; }
var divide10 = selective(divide, { a: 10 });
console.log(divide10(2)); // 5 (a = 10, b = 2)
var divideBy3 = selective(divide, { b: 3 });
console.log(divideBy3(9)); // 3 (a = 9, b = 3)

Having to explicitly name arguments to apply leads to cleaner code in my opinion. On the other hand, heroin has a limitation. Successful selection depends on the original argument names, which are usually mangled during minification. I could add support for name annotations, similar to the dependency injection in AngularJs, so if this is needed, let me know via new issue.

Related