Essence of functional programming

FP in plain JavaScript using partial application, composition, closures and higher-order functions.

The power of functional programming is in the way it removes code complexity by splitting a large chunk of code into an assembly of tiny simple functions. JavaScript might not be the most pure functional language, but it has more than enough language constructs to allow quick functional programming even without using excellent libraries like lodash or Ramda.

Take a simple problem: print a given string N times, pausing between each print.

Here is a typical solution:

1
2
3
4
5
6
7
8
9
10
11
function timedPrint(character, N, interval) {
var counter = 0;
var intervalId = setInterval(function () {
console.log(character);
counter += 1;
if (counter >= N) {
clearInterval(intervalId);
console.log('done printing', character, N, 'times');
}
}, interval);
}

One can verify that this function works, each character a is printed 1 second after the previous one.

timedPrint('a', 3, 1000);
a
a
a
done printing a 3 times

The current implementation is ok, but it mixes multiple aspects in a single function timedPrint. I will mark each line with one of the aspects: counting, printing or time

function timedPrint(character, N, interval) {
  var counter = 0; // counting
  var intervalId = setInterval(function () { // time
    console.log(character); // printing
    counter += 1; // counting
    if (counter >= N) { // counting
      clearInterval(intervalId); // time
      console.log('done printing', character, N, 'times');
    }
  }, interval); // time
}

This simple example does not include all possible aspects, for example I often perform argument checks at the start of each function, a practice I call paranoid programming.

Separate printing

JavaScript has the main feature of any functional language: functions are first class objects, they can be passed as arguments to and returned from other functions. Let us separate the first aspect from the main function - we will move the printing feature into a separate function. We will also pass the function to the timedPrint as argument instead of character.

function timedPrint(workload, N, interval) {
  var counter = 0;
  var intervalId = setInterval(function () {
    workload();
    counter += 1;
    if (counter >= N) {
      clearInterval(intervalId);
      console.log('done printing', N, 'times');
    }
  }, interval);
}
timedPrint(function print() { // 1
  console.log('a');
}, 5, 1000);

Notice how the timedPrint is no longer coupled to the printing operation or character argument. Instead it just executes the given workload function.

We constructed the print function on the fly using functional expression in line // 1. While this is ok, we can make it more obvious using another built-in JavaScript feature: partial application. Instead of creating an entire function just to call console.log('a'); we can create a function on the fly using Function.prototype.bind method.

var printA = console.log.bind(console, 'a');
timedPrint(printA, 5, 1000);

At this point it makes sense to rename timedPrint to timed because the printing is no longer hardcoded inside the function.

function timed(workload, N, interval) {
    ...
}
timed(printA, 5, 1000);

Because timed function takes another function as an argument, timed is now a higher-order function.

Separate the counting aspect

We are executing the given workload function N times. Can we remove the counting aspect from the timed function? Easily: instead of counting inside timed, let us pass another function that tells us when the interval should stop. The counting can be implemented inside that function.

function timed(workload, isDone, interval) {
  var intervalId = setInterval(function () {
    workload();
    if (isDone()) { // 1
      clearInterval(intervalId);
      console.log('done executing workload');
    }
  }, interval);
}
var printA = console.log.bind(console, 'a');
function nTimes(N) {
  var counter = 0;
  return function () { // 2
    counter += 1;
    return counter >= N;
  };
}
timed(printA, nTimes(5), 1000);

The termination logic is implemented in nTimes function using 2 JavaScript features:

  1. It returns another anonymous function (line // 2) to be called from timed (line // 1) to determine if we have executed workload() as many times as necessary.
  2. nTimes keeps N parameter available to be compared to counter via closure. See my explanation of JavaScript closures.

The remaining timed function is much simpler because it only deals with the timer interval logic. It is also more powerful because we can pass different workload and isDone functions. For example, let us print a string again every second for 10 seconds.

1
2
3
4
5
6
7
8
9
function nSeconds(N) {
N *= 1000; // to ms
var started = Number(new Date());
return function () {
var elapsed = new Date() - started;
return elapsed >= N;
};
}
timed(printA, nSeconds(10), 1000);

In general any place you pass a primitive value and then use it for some unrelated aspect (like passing a character and then printing it), you probably want to pass a function that encapsulates both the primitive value and the logic.

Exposing the desired API function to the users

What if my users do not care about the full power that the rewritten timed allows? What if they only care about having the original timedPrint API function? The functional programming makes it simple to assemble any desired public API on the fly from the individual pieces. For example, using CommonJS module definition (for Node)

timed-print.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function timed(workload, isDone, interval) {
var intervalId = setInterval(function () {
workload();
if (isDone()) {
clearInterval(intervalId);
console.log('done executing workload');
}
}, interval);
}
function nTimes(N) {
var counter = 0;
return function () {
counter += 1;
return counter >= N;
};
}
// assemble the public API function
module.exports = function timedPrint(character, N, interval) {
var print = console.log.bind(console, character);
var isDone = nTimes(N);
timed(print, isDone, interval);
};
// index.js
var timedPrint = require('./timed-print');
timedPrint('a', 3, 1000);

We can call the assembled timedPrint function inside timed-print.js file an example of functional composition - a larger function is assembled from smaller ones.

Defending a public API function

So far we have not used any 3rd party libraries, just plain JavaScript. I will show only a single additional library that can be used to quickly check passed arguments. Again, this is an example of functional composition because it combines the individual predicate functions and the original function to return a new higher-order function.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// timed and nTimes functions as before
// public function without any input checks
function timedPrint(character, N, interval) {
var print = console.log.bind(console, character);
var isDone = nTimes(N);
timed(print, isDone, interval);
};
// defend public function from the invalid inputs
require('lazy-ass');
var check = require('check-more-types');
module.exports = check.defend(timedPrint,
check.unemptyString, 'need character / string to print',
check.positiveNumber, 'number of times to print should be positive',
check.positiveNumber, 'invalid timer interval');

The defense is done using check.defend method from check-more-types library. If we pass valid arguments everything is working correctly. If we try to pass invalid arguments, for example a string where N is expected, we will get an error.

// index.js
var timedPrint = require('./timed-print');
timedPrint('a', 'three', 1000);
// output
Error: Argument 2: three does not pass predicate: number of times to print should be positive

Conclusion

Plain JavaScript allows writing small, simple to understand and test, composeable functions. It has all the features necessary to create expressive and robust code using only a couple of language features: first order functions, closures, partial argument binding. To learn more read my other blog posts on functional JavaScript, and check out the excellent presentation Functional Programming by Scott Sauyet.