Functional programming in Javascript

Stephen Young

Contents

  1. What is functional programming?
  2. Why is it useful?
  3. Patterns in functional programming
  4. Further Resources
  5. Test-Driven Tutorial

What is functional programming?

It's all about functions!

You probably do "functional" things every day…

Creating functions


var returnFunc = function(z) {
    return function(a) {
        return a * z;
    };
};
                    

Passing functions to other functions


var funcAsArgument = function(a, func) {
    return func(a);
};
                        

Using them to transform data


var newArray = [1, 2, 3].map(function(v, i) {
    return v + 2;
});

console.log(newArray) // [3, 4, 5]
                        

The environments in which they are created (closures)


var func = function() {
    var a = 3;
    return function(b) {
        return b * a;
    };
};

var closure = func();
closure(4) // 12
                        

All of these examples demonstrate one of the key features of functional programming:

Functions are values

They are first-class!

Three key concepts:

  1. Computation as the application of functions
  2. Statelessness
  3. Immutable data

In functional programming computation is done by applying functions:


// Functional computation
var numbers = [10, 15, 20, 25, 30];
var addNumbersInArray = numbers.reduce(function(a, b) {
    return a + b;
});

console.log(addNumbersInArray) // 100
                    

// Non-functional computation
var numbers = [10, 15, 20, 25, 30];
var addNumbersInArray = 0;

for (var i = 0, ln = numbers.length; i < ln; i += 1) {
    addNumbersInArray += numbers[i];
}

console.log(addNumbersInArray) // 100
                    

Functional programming is declarative:

I do not care how you calculate the result I'm just going to give you this function and let you deal with it.


var isEveryElementInThisArrayTrue = [true, true, false].every(function(v) {
    return v === true;
});

console.log(isEveryElementInThisArrayTrue) // false
                    

Non-functional programming (or object-oriented programming) is imperative:

I will give you step-by-step instructions of how I want you to achieve the result.


var isEveryElementInThisArrayTrue = function(arrayOfBooleans) {
    var result;
    for (var i = 0, ln = arrayOfBooleans.length; i < ln; i += 1) {
        result = arrayOfBooleans[i] === true;
    }
    return result;
};

console.log(isEveryElementInThisArrayTrue([true, true, false])) // false
                    

Functional programming is about being predictable.

Every time I call a function with the same arguments I will expect to get the same result:


// Every call to 'increment' produces the same result
var number = 1;
var increment = function(n) {
    return n + 1;
};
increment(number); // 2
increment(number); // 2
increment(number); // 2
                    

This is known as referential transparency

And can be described as a stateless approach to programming.

Object-oriented programming is stateful:


var number = 1;
var increment = function() {
    return number += 1;
};
increment(); // 2
increment(); // 3
increment(); // 4
                    

The state of the program's environment is changed.

Object-oriented programming works by changing program state to produce answers.


// To get an answer we continuously change the value of 'addNumbersInArray'
var numbers = [10, 15, 20, 25, 30];
var addNumbersInArray = 0;

for (var i = 0, ln = numbers.length; i < ln; i += 1) {
    addNumbersInArray += numbers[i];
}

console.log(addNumbersInArray) // 100
                    

Functional programming uses function application to produce answers without changing state.


var numbers = [10, 15, 20, 25, 30];
var addNumbersInArray = function(numbersArray, i, result) {
    if (i >= numbersArray.length) {
        return result;
    } else {
        return addNumbersInArray(numbersArray, i + 1, result + numbersArray[i]);
    }
};

console.log(addNumbersInArray(numbers, 0, 0)); // 100
                    

This is an example of recursion (a function that calls itself).

Functional programming is about working with data that does not change.


var x = 3;
x = 4; // Illegal with functional programming
                    

var array = [1, 'one', 2, 'two', 3];
array.push('three'); // Illegal with functional programming
                    

var obj = {a: 1, b: 2, c: 3};
obj.d = 4; // Illegal with functional programming
                    

All of these mutate data and are stateful operations.

Functional programming advocates immutable data.


var x = 3;
var y = x + 1;
                    

var array = [1, 'one', 2, 'two', 3];
var array2 = array.concat('three'); // Produces a new array and doesn't change the old one
                    

var object = {a: 1, b: 2, c: 3};
var object2 = Object.create(obj, {d: {value: 4, enumerable: true}}; // Produces a new object and doesn't change the old one
                    

All of these are stateless operations.

Why is it useful?

  1. A more elegant way of writing programs
  2. Programs that are rigorous and easier to test
  3. Programs that are easier to maintain

// This reads nicer
var isEveryElementInThisArrayTrue = [true, true, false].every(function(v) {
    return v === true;
});

console.log(isEveryElementInThisArrayTrue) // false
                        

// than this
var isEveryElementInThisArrayTrue = function(arrayOfBooleans) {
    var result;
    for (var i = 0, ln = arrayOfBooleans.length; i < ln; i += 1) {
        result = arrayOfBooleans[i] === true;
    }
    return result;
};

console.log(isEveryElementInThisArrayTrue([true, true, false])) // false
                        

var anyFunction = function() {
    var x = 5;
    var data = {
        a: 5,
        b: 6
    };
    anotherFunction(data);
    x = 5 + data.a;
};
                        

Patterns in functional programming

Continuation Passing Style (CPS)

Uses continuations (functions) to represent the next stage of computation:


$.get('url', function(data) {
    console.log(data);
});

http.get('url', function(data) {
    console.log(data);
});
                        

A more complex example to compute the factorial of n.

If n is 3 then the factorial of n is 1 * 2 * 3 = 6


var CPSFactorial = function(n, continuation) {
    if (n < 2) {
        return continuation(n);
    } else {
        var new_continuation = function(v) {
            var result = v * n;
            return continuation(result);
        };
        return CPSFactorial(n - 1, new_continuation);
    }
};

CPSFactorial(5, function(v) {
    return v;
}); // 1 * 2 * 3 * 4 * 5 = 120
                        

Partial application

Pre-filling an argument to a function.

The simplest way is with bind:


var bindExample = function(a, b) {
    return a / b;
};

var test = bindExample.bind(null, 3);

test(4); // 0.75
                        

However, we can write our own:


var bind = function(a, func) {
    return function() {
        return func.apply(null, [a].concat(Array.prototype.slice.call(arguments)));
    };
};

var bindExample = function(a, b) {
    return a / b;
};

var test = bind(3, bindExample);

test(4); // 0.75
                        

Currying

Similar to partial application, however, …

A curried function will return a new function until it receives all of its arguments.


var curry = function(fn) {
    var result = function() {
        var newArgs = Array.prototype.slice.call(arguments, 0);

        if (newArgs.length >= fn.length) {
            var finalResult = fn.apply(null, newArgs);
            return finalResult;
        } else {
            return Function.prototype.bind.apply(result, [null].concat(newArgs));
        }
    };

    return result;
};

var addNumbers = curry(function(arg1, arg2, arg3) {

    return arg1 + arg2 + arg3;
});

var curry1 = addNumbers(4); // function
var curry2 = curry1(5); // function

console.log(curry2(6)); // 15

var curryA = addNumbers(10, 20); // function

console.log(curryA(30)); // 60

var curryI = addNumbers(); // function

console.log(curryI(1, 2, 3)); // 6
                    

http://codepen.io/anon/pen/zgGli

Further Resources

Test-Driven Tutorial

https://github.com/stephendeyoung/functional-js-assessment