Partial application for options object

Bind some properties in the options object.

Partial application review

JavaScript Function.prototype.bind performs both context binding and partial application. Let us review the partial application: one can bind argument values in the left to right order, creating a new function.

partial application example
1
2
3
4
5
6
function add(a, b) {
return a + b;
}
console.log(add(2, 3)); // 5
var add10 = add.bind(null, 10);
console.log(add10(3)); // 13

We have created a new function add10 from the existing function add by using the provided bind method. Calling add10(3) is equivalent to calling add(10, 3) - the single provided value 10 was bound to the first argument a. We could even create a function that binds all arguments

binding all arguments example
1
2
var add2to3 = add.bind(null, 2, 3);
console.log(add2to3()); // 5

Limitations

In order to bind the function's arguments one has to list the arguments separately. Thus we could bind the first argument a in the function add(a, b). I often find that any function with more than 2 arguments needs to accept an options object instead of separate arguments. This simplifies the API, removes the need to remember the arguments order and allows easy argument defaults. For example,

options example
1
2
3
4
5
6
7
8
9
10
11
12
function calc(options) {
options = options || {};
switch (options.op) {
case '+':
return add(options.a, options.b);
case '-':
return add(options.a, -options.b);
default:
throw new Error('Unknown calc operand in ' +
JSON.stringify(options));
}
}

We have implemented a more general API for addition and subtraction reusing the same addition function

1
2
3
4
5
6
7
8
9
10
11
console.log(calc({
op: '+',
a: 2,
b: 3
})); // 5
// key order does not matter
console.log(calc({
a: 10,
op: '-',
b: 3
})); // 7

Note that instead of remembering the order of arguments (is it operand then arguments or is the operand between them?), we just pass a single options object.

Now we encounter a problem: we can no longer bind some values to the calc arguments using the partial application mechanism! How can we create an adder function for example? We need to be able to fill a property inside the options object.

1
2
var add10 = add.bind(null, 10);
var adder = calc.bind(?);

We need an utility method that could fill some values in the single options object.

obind

I wrote obind that does just that: partial application for options object.

making an adder
1
2
3
4
5
6
var obind = require('obind');
var adder = obind(calc, { op: '+' });
console.log(adder({
a: 2,
b: 3
})); // 5

One can even bind multiple properties at once, or in several steps. For example, we can make adder10 by binding a property.

1
2
var adder10 = obind(adder, { a: 10 });
console.log(adder10({ b: 30 })); // 40

Note that obind only does the partial application and not the context bind, thus no need to pass the null value as the first argument.

I tried to make obind behave the same way as its functional inspiration. For example, even if no arguments are provided, it returns a new function

1
2
3
4
5
6
7
8
9
10
11
// Function.prototype.bind always returns new function
var addNone = add.bind(null);
console.assert(addNone !== add);
console.log(addNone(3, 4)); // 7
// obind returns new function
var adderNone = obind(adder, {});
console.assert(adderNone !== adder);
console.log(adderNone({
a: 10,
b: 20
})); // 30

There are just two obind-specific differences

1: One need to provide at least an empty object when using obind, for example obind(adder, {}), otherwise it throws an error. I made this choice to simplify the debugging; if there is nothing or a non plain JavaScript object, something is wrong.

2: I make a deep clone of the options object to prevent changing the bound values. Otherwise the values inside the first object could be changed later leading to very unexpected behavior.

one cannot change applied values
1
2
3
4
5
var a2 = { a: 2 };
var adder2 = obind(adder, a2);
console.log(adder2({ b: 30 })); // 32
a2.a = 100;
console.log(adder2({ b: 30 })); // still 32

Related