I described point-free programming in a separate blog post entry. This blog post gives 2 more examples that come up in my day to day programming.
Adapting callback to extra arguments
In most situations, the callback function requires more arguments that the caller supplies.
For example sum
needs 2 arguments, while foo
supplies only 1
1 | function sum(a, b) { |
We usually solve this problem using partial application with built-in JavaScript method
1 | var add10 = sum.bind(null, 10); // add10 needs only 1 argument |
In other cases, we want to limit and not pass arguments from the caller to the callback function.
For example parseInt(str, radix)
requires more (or non-default) arguments that we provide, leading to the confusion
1 | ['1', '2', '3'].map(parseInt); // [1, NaN, NaN] |
Typically I solve this problem by binding (partial application), providing some values to the arguments.
For example, I can create a new function around parseInt
that always provides 10 as the second argument
(using spots).
1 | var S = require('spots'); |
What if we want to let parseInt
pick a radix? We need to create a callback that ignores values passed
from the caller. We could create a small anonymous adaptor function, whose only purpose is to call parseInt
1 | ['1', '2', '3'].map(function (x) { |
This is not point-free approach I like, instead this is verbose and error-prone extra code.
I wrote a small function ignore-argument that skips certain arguments and allows quickly adapting a function to be called point-free from the chatty caller.
1 | var ignore = require('ignore-argument'); |
The same approach can be used to skip the first argument when setting callback for Angular event listener on the scope object. Instead of this:
1 | function onFoo(a, b) { ... } |
We adapt onFoo
to ignore the first argument in place:
1 | var ignore = require('ignore-argument'); |
Partial application for point-free methods
A common case in my code lately has been calling the same method, with the same first argument, but varied second argument. For example when adding the same class to bunch of elements by selector we can write:
1 | var selectors = ['.navbar', '#footer', '#help']; |
Can we avoid creating addLiteClass
adaptor and use point-free approach? Yes, and here is how it would work.
Let us take smaller example, without jQuery call $(...)
to simplicity
1 | var objects = [ |
We are calling method foo
, and passing two arguments "A", k
. Let us use one of my favorite libraries
Ramda to call method 'foo' and apply "A"
var fooA = R.invokerN(2, 'foo')('A');
objects.forEach(function (object, k) {
fooA(k, object);
});
The temporary function fooA
will call the actual method .foo
but needs it the second argument and the actual
instance. We cannot use it to use in a point-free forEach
callback, because the arguments are flipped.
Since there are only two arguments
(anything with more than 2 arguments requires an explicit adaptor function in my opinion),
we can flip the order using R.flip
function. This allows us to write a point-free callback in this case
objects.forEach(
R.flip(R.invokerN(2, 'foo')('A'))
);
One can argue if this is a worse coding style than an explicit adaptor version, but I wanted to show that this is possible.