Point-free programming is not pointless.

Eliminate variables and functions via flexible selective application.

This blog post has been submitted for reddit discussion.

I like using array iterators. For example to multiply every number by 2 into a new array I would write

1
2
3
4
[1, 2, 3].map(function (x) {
return 2 * x;
});
// [2, 4, 6]

Because I love functional programming and readability, I will reuse the function that multiplies given argument by 2

1
2
3
4
5
6
7
function double(x) {
return 2 * x;
}
[1, 2, 3].map(function (x) {
return double(x);
});
// [2, 4, 6]

Notice that argument x is passed directly from iterator callback function to double function. We can shorten this to:

1
2
3
4
5
function double(x) {
return 2 * x;
}
[1, 2, 3].map(double);
// [2, 4, 6]

We just eliminated 1 function and most importantly 1 variable. Method Array.prototype.map calls double directly, passing each item, index and array directly without going through the intermediate dummy function.

This elimination of unnecessary variables that are just passed from one function to another is called point-free programming 1. It eliminates unnecessary code, leading to smaller and simpler code. I also believe that it leads to more robust code by eliminating possible sources of misspelled variable names.

What if our callback function does not match exactly the signature of the array iterator function? For example, what if instead of multiplying (associative) by a number, what if we wanted to divide by a constant?

function divideBy3(x) {
    return x / 3;
}
[3, 6, 9].map(divideBy3);
// [1, 2, 3]

Why do we need to explicitly define double and divideBy3 functions? We could construct double on the fly by partially applying first argument

function mul(a, b) {
    return a * b;
}
[1, 2, 3].map(mul.bind(null, 2));
// [2, 4, 6]

Using mul.bind(null, 2) gives a function that calls mull(2, _) where the first argument is applied and will have value 2. The second argument remains free and will be provided by the array iterator.

The same approach does not work with division by 3, because we need to apply second argument!

function div(a, b) {
    return a / b;
}
[3, 6, 9].map(div.bind(null, 3));
// [1, 0.5, 0.3333]

Instead of getting partially applied function that divides by 3, we got a function that divides 3 div(3, _). Of course, we could write a partial right function, or use _.partialRight. Sometimes though the argument we want to bind is the middle one!

To avoid this problem and allow convenient point-free programming style in JavaScript I wrote spots. It only has single function that partially applies a given function, and leaves spots unbound. Just use the reference to the function itself as a placeholder.

var S = require('spots');
[3, 6, 9].map(S(div, S, 3));
// [1, 2, 3]

Expression S(div, S, 3) returns new function that will call div(_, 3). Thus S(div, S, 3)(6) is the same as div(6, 3).

Spots is one of the libraries that allow selective partial application.

parseInt

The point-free style allows for quick solution to another curious problem described in The Madness of King JavaScript.

['1', '2', '3'].map(parseFloat); // [1, 2, 3]
['1', '2', '3'].map(parseInt); // [1, NaN, NaN]

This is due to Array.prototype.map passing 3 arguments to the callback function: the item, the index (starting with 0) and the reference to the array itself. Our previous functions mul and div ignored second and third arguments. Function parseFloat also only needs first argument, but function parseInt expects two arguments: a string and a radix. If radix is 0, base 10 is assumed. Thus the first pass of the map calls parseInt('1', 0) producing 1, but the second pass calls parseInt('2', 1) which is impossible, producing NaN.

Typically I use unary function adaptor to avoid this problem, or Ramda.map iterator that does NOT pass index. Using spots allows me to avoid the problem by placing placeholder

['1', '2', '3'].map(S(parseInt, S, 10));
// [1, 2, 3]

Pipelining

If you use spots with my functional-pipeline library you can write even more complex expressions, that leave placeholders that will be filled, causing other functions to be called, etc.

For example, lets us create a function that will convert then divide by 3.

var fp = require('functional-pipeline');
var S = require('spots');
function div(a, b) {
    return a / b;
}
['3', '6', '9'].map(fp(
    S(parseInt, S, 10),
    S(div, S, 3)
));
// [1, 2, 3]

The function returned from fp( S(parseInt, S, 10), S(div, S, 3) ) has a single free argument (the first placeholder S in parseInt). Once we provide a value to this placeholder, we immediately have a value to be plugged into the second step S(div, S, 3). Here is the pipeline by itself.

var convertAndDivideBy3 = fp( S(parseInt, S, 10), S(div, S, 3) )
convertAndDivideBy3('300');
// 100

Conclusion

Eliminating unnecessary functions using flexible argument binding cuts down on amount of code necessary. Shorter code with fewer moving parts (fewer variables) is simpler to read and test.

Update 1

I wrote rule potential-point-free that can detect functions that can be made point-free.

/* eslint potential-point-free:1 */
function print(x) {
  console.log(x);
}
[1, 2, 3].forEach(function printX(x) {
  print(x);
});

Running eslint

$ eslint --rulesdir .. test.js
test.js
   7:18  warning  printX   potential-point-free
✖ 1 problem

One has to be careful to avoid passing extra arguments in this case. I would recommend switching to Ramda's iterator and transform the above example

R.forEach(print, [1, 2, 3]);

Update 2

Sometimes the signatures of the callback and the caller do not match and we have to use adapt the callback function. If the callback needs more information, we use selective application. But if the caller has more arguments than the callback, we can use ignore-argument function. For example, if the caller passes a string first, and our work function does not need it

function foo(a, b) {
    console.assert(arguments.length === 2);
}
function bar(cb) {
  // calls cb with 3 arguments!
  cb('foo', 'a', 'b');
}

We cannot just pass foo to bar in point-free style

bar(foo); // does not work, throws an error

We typically see small anonymous adaptor functions

bar(function (first, a, b) {
    // first argument is ignored
    return foo(a, b);
});

Using ignore-argument we can create point-free code in single line

bar(ignoreArgument(foo, true));

You can ignore any number of arguments, for example to ignore first and third

bar(ignoreArgument(foo, true, false, true));

We can even solve the same parseInt problem by ignoring the radix (instead of specifying 10).

['1', '2', '3'].map(ignoreArgument(parseInt, false, true)); // [1, 2, 3]

Update 3 - point-free by composition

So far, the examples only showed point-free callbacks. Point-free also can be achieved using the composition of functions. To review, we can write a function to login a user when a form is submitted by telling the computer the explicit steps.

1
2
3
4
5
// imperative
function authenticate(form) {
var user = toUser(form);
return logIn(user);
}

Notice that the imperative form declared an input variable form and uses a temporary variable user to work. We can shorten this and avoid the temporary variable

1
2
3
function authenticate(form) {
return logIn(toUser(form));
}

Now we notice that all authenticate does is call 2 functions, passing the input form to the toUser first, then taking the result and passing it to logIn. Applying logIn to the result of toUser is equivalent to creating a new function that is a composition of logIn and toUser if the functions are pure. Most functional libraries include a general compose function, and the above authenticate can be rewritten to avoid any variables!

1
2
// declarative point-free using compose
var authenticate = compose(logIn, toUser);

The example is taken from Mostly adequate guide to FP.