Functional programming in the real world: A Javascript example

Functional programming in the real world: A Javascript example

Functional programming is a style that stresses the mutation, combination and use of functions. Even though Javascript

Functional programming is coming back. Originally relegated to the annals of computer science academia, functional programming has had a recent renaissance. In this article we show how can be used in the real world.

Functional programming javascript

Functional programming is a style that stresses the mutation, combination and use of functions. Even though Javascript is not a pure functional language, it treats functions as objects (first-class functions).

This means that, in Javascript, functions can be passed as arguments to other functions, assigned to variables and created dynamically inside a function.

Higher-order functions

These are functions that accept other functions as arguments and/or return new functions. Below we will show a very simple example: a function accepts a number as argument and returns a new function that always returns that same number. Then we will use it to create two functions: return5 and return42, which always return 5 and 42, respectively.

// Returns a function that always returns x
function returnX(x) {
  return function() {
    return x;
  }
}

// return5 is a function that always returns 5
var return5 = returnX(5);

// return42 is a function that always returns 42
var return42 = returnX(42);

console.log(return5()); // prints 5
console.log(return42()); // prints 42

This works because the functions “remember” the context in which they were created, so when the new function is created they remember the value of x and they can return it correctly.
The following example shows a function that accepts an argument and returns a new function.

This new function also receives an argument and returns the sum of them:

function plus(x) {
	return function(y) {
		return y+x;
	}
}

var plus10 = plus(10);
console.log(plus10(5)); // Prints: 15

In this case, the function we return accepts a new argument; this argument will be added to the variable x that has been saved in the context of the function.

The following example shows a function which, apart from returning a function, receives a function as argument.

function consoleLogFunction(prefix, fn) {
  return function(x) {
    var result = fn(x);
    console.log(prefix, result);
    return result;
  }
}

var consoleLogSqrt = consoleLogFunction('sqrt =', Math.sqrt);
consoleLogSqrt(144); // Prints: sqrt = 12
consoleLogSqrt(256); // Prints: sqrt = 16

function square(x) {
  return x*x;
}

var consoleLogSquare = consoleLogFunction2('square =', square);
consoleLogSquare(12); // Prints: sqrt = 144
consoleLogSquare(16); // Prints: sqrt = 256

In the previous example, the function consoleLogFunction receives two arguments: prefix and fn. Prefix is a string which will be shown throughconsole.log, together with the result obtained from executing fn. The problem posed by this implementation is that consoleLogFunction returns a function which accepts only one argument; therefore, if we want to do the same using a function that receives two arguments, we will have to create a new function:

function consoleLogFunction2Arguments(prefix, fn) {
  return function(x, y) {
    var result = fn(x, y);
    console.log(prefix, result);
    return result;
  }
}

var consoleLogPow = consoleLogFunction2Arguments('Pow = ', Math.pow);
consoleLogPow(2, 8); // Prints: Pow = 256

This is not the best solution, since we will have to create a consoleLogFunction for each argument we need. In order to generalize our function, first we have to see the following.

Call, apply and arguments

Javascript functions are also objects, and they have some associated methods, such as call and apply. Both methods are used to execute functions, and both of them receive as first argument the object that will be used as this inside the function. The difference between them is that call receives the arguments as a list, and apply receives them as an array.

In the following example we can see those differences:

Math.sqrt.call(null, 144); // 12
Math.sqrt.apply(null, [144]); // 12

Math.max.call(null, 1, 3, 2); // 3
Math.max.apply(null, [1, 3, 2]); // 3

function printThis() {
  console.log(this);
}

printThis.call({a: 1}); // Prints: { a: 1 }
printThis.apply({a: 1}); // Prints: { a: 1 }

Additionally, all the functions have access to a local variable called arguments which makes reference to an object with all the arguments passed to the function. First of all, let us see the structure of the object arguments:

function printArgumentsObject() {
  console.log(arguments);
  console.log('arguments.length: ' + arguments.length);
}

// Prints:
// { '0': 3, '1': 2, '2': 1, '3': 'test' }
// arguments.length: 4
printArgumentsObject(3, 2, 1, 'test');

As we can see, the structure of the object arguments resembles an array, we can access arguments individually as arguments[n], and we can obtain the number of arguments passed using arguments.length.

If we want to turn the object arguments into an array, the recommended way is as follows:

function printArgumentsAsArray() {
    var args = new Array(arguments.length);
    for(var i = 0; i < args.length; i ++) {
        args[i] = arguments[i];
    }

    console.log(args);
}

// Prints:
// [ 3, 2, 1, 'test' ]
printArgumentsAsArray(3, 2, 1, 'test');

Now we are ready to create the function consoleLogFunction in a generic way:

Application examples

Spy functions

Spy functions modify other functions to save information about their execution, such as the number of times it was executed and a history of the arguments it received.
In the following example, we create a Spy function that receives an object and a method inside the object. Then it modifies and wraps this method inside a context, where we save the number of times it was executed (ret.count) and the history of the arguments it received (ret.argHistory).

function Spy(target, method) {
  var ret = {
    count: 0,
    argHistory: []
  };

  var oldMethod = target[method];
  target[method] = function() {
    ret.count ++;
    ret.argHistory.push(arguments);
    return oldMethod.apply(this, arguments);
  }

  return ret
}

module.exports = Spy;

var spy = Spy(console, 'error');
console.error('1');
console.error('3');
console.error('2');

// Prints: 3
console.log(spy.count);

// Prints: [ { '0': '1' }, { '0': '3' }, { '0': '2' } ]
console.log(spy.argHistory);

Memoization

Memoization is a technique used to optimize pure functions (the return value only depends on the arguments and there are no side-effects), caching results obtained according to their arguments. Therefore, every time we execute a “memoized” function, first it checks if the result is in the cache and returns it without executing the function; if it is not in the cache, the function is executed and the result is saved in the cache for future calls.

By using higher-order functions, it is possible to write a function that can “memoize” any other functions, where the cache is an object existing within the context of the returned function. Below we will show how to write this function, as well as a use example:

function memoize(func) {
  var cache = {};
  var slice = Array.prototype.slice;

  return function() {
    // Get arguments as array as seen before
    var args = new Array(arguments.length);
    for(var i = 0; i < args.length; i ++) {
        args[i] = arguments[i];
    }

    // Check cache
    if (args in cache) {
      console.log('Cache hit')
      return cache[args]; // Return cached value
    } else {
      console.log('Cache miss')
      return (cache[args] = func.apply(this, args));
    }
  }
}

var memoizedPow = memoize(Math.pow);
memoizedPow(2, 8); // Cache miss
memoizedPow(2, 4); // Cache miss
memoizedPow(2, 8); // Cache hit

Next we will see some higher-order functions provided by Javascript which are very useful.

Map

Map is an array method that receives a function which will be executed once per each element in the array, and it will receive this element as parameter. Then it will return a new array with all the values returned by the function.

In the following example we first use map together with the function Math.sqrt to return an array with the square root of all the elements. Then we use map to return an array with all the elements divided by 2.

var arr = [144, 256, 1024];

// Prints: [12, 16, 32]
console.log(arr.map(Math.sqrt));

var half = arr.map(function (element) {
  return element/2;
})
// Prints: [72, 128, 512]
console.log(half);

Filter

The filter method creates a new array with all the elements that pass the condition implemented by the function passed as argument.

In the following example, we filter the even numbers out of the array.

var arr = [1, 3, 4, 6, 4, 7];

even = arr.filter(function (element) {
  return element%2 === 0;
});

// Prints: [ 4, 6, 4 ]
console.log(even);

Every, Some

The every method checks that all the elements of the array meet a given condition, whereas the some method checks that at least one element meets the condition.

In the following example we use every and some with different arrays, by means of a function that checks if a number is positive.

var allPositives = [1, 2, 3];
var oneNegative = [3, -1, 2];
var allNegatives = [-2, -1];

function positive(x) {
  return x > 0
}

console.log(allPositives.every(positive)); // true
console.log(allPositives.some(positive)); // true

console.log(oneNegative.every(positive)); // false
console.log(oneNegative.some(positive)); // true

console.log(allNegatives.every(positive)); // false
console.log(allNegatives.some(positive)); // false

Reduce

The reduce method applies the function between an inside accumulator and each element in the array; then, it returns the accumulator and reduces the array to only one value.

In this example we will show you how to use reduce to obtain the sum of all the elements of the arrangement:

var arr = [1, 2, 3];

function sum (a, b) {
  return a + b;
}

// Prints: 6
console.log(arr.reduce(sum));

Further information

Official reference for native methods:

The book Eloquent Javascript, available online, has a chapter on higher-order functions:

Eloquent Javascript – Chapter 5- Higher-order functions