Functional Programming in JavaScript, Part 1: The Unit

Thiery Michel
Thiery MichelMarch 14, 2018
#functional-programming#tutorial#js

This is part 1 in a series on Functional Programming in javascript

This is the first article of a series on functional programming (FP) in JavaScript. In this series, I will talk about pure functions, composition, currying, monoids, functors, etc... And I will even try to work my way towards the elusive monad.

Monad, Gonad, Mossad?

If you've already of functional programming, you've probably stumbled upon the term "monad". Apparently, monads appear related to a lot of things in day-to-day code: promises, arrays, observables, etc. Perhaps you've searched for a definition, and then you most certainly did not understand anything about it. I mean, seriously:

A monad is a construction that, given an underlying type system, embeds a corresponding type system into it. This monadic type system preserves all significant aspects of the underlying type system while adding features particular to the monad.

Or, even better:

A monad is just a monoid in the category of endofunctors.

I am so dizzy

Let's take a deep breath, and forget about those weird definitions. For now, let's just say that the monad is a pattern in functional programming. But before we can see what pattern it is, let's first introduce functional programming.

Introduction To Functional Programming

Functional programming is a programming paradigm that is declarative. You describe what you want, as opposed to imperative programming, where you say how you want the computer to work.

More precisely, in true functional programming, there is no control flow. No for, while, if, try, throw or catch statements. Functional Programming languages provide equivalents for these:

  • Instead of for or while, we use Array.map or Array.reduce,
  • Instead of if, we use ternary expressions condition? true statement : false statement,
  • And instead of throwing an error, we return it as the result of a function.

Also, functional programming forbids side effects. To be more precise, functional programming can have side effects, but they get isolated from the rest of the code. And in case you're wondering, a side effect is anything that modifies the state outside of the function, for instance sending an HTTP request, changing the information displayed on a screen, recording a log, or calling a function which has such side effects. Yes, you need side effects all the time.

Lastly, functional programming enforces immutability. That means that a function should never modify its arguments. For instance, [1,2,3].push(4) mutates the initial array and returns the new length. On the other hand, [1,2,3].concat(4) returns a new array with the values [1,2,3,4]. It doesn't mutate the initial object.

Why Bother With Functional Programming?

Why not use side effects and mutate everything that we want? After all, we've been doing imperative programming for decades.

The fact is, programming without side effects or mutations makes the code more predictable, and less error prone. It also makes it easy to run the code in parallel, and benefit from the gazillion CPUs sitting in today's computers (instead of just 1 core for a standard Node.js program for instance).

FP code is more predictable because the execution does not depend on the context. Functions never throw an error, and always return the same result given the same input.

Code with side effect

var global = 0;
const square = x => {
    global++; // evil side effect
    return x * x;
}
const main = () => {
    var res = square(5);
    res += global;
    return res;
}

main(); // 26
main(); // 27

The same without side effect nor mutation

const square = x =>  x * x;

const main = (g) => {
    return square(5) + g;
}

const global = 1;
main(global); // 26
main(global + 1); // 27

Aren't you tired of encompassing every function call into a try/catch? Aren't you tired of hunting bugs caused by a side effect triggered by another function?

If you are, keep reading, because this may soon become a revelation.

Pure Functions

I will start with the basic, literally, by talking about the unit of code in functional programming, a.k.a. the function.

The unit

In FP, functions have to be pure. A pure function has the following characteristics:

  • Its result depends only on its input
// impure
var changeResult = true;
const a => changeResult ? a + a : a;

// pure
const fn = (a, changeResult)  => changeResult ? a + a : a;
  • It does not mutate any variable outside of its body
// impure
var nbCall = 0;
const fn = () => {
    // do something
    nbCall ++;
};

// pure
const fn = (arg, nbCall) => {
    // do something

    return {
        result,
        nbCall: nbCall++,
    };
};
  • It does not mutate its arguments
// impure
const inc = i => i++;

// pure
const inc = i => i + 1;
  • It never throws an error

Throwing error break the flow of execution, and the function has no control as to where the error will be caught if it is indeed caught. Throwing an error is like throwing a bomb, in the hope that others will be able to handle it. And it forces users of the function to wrap it in a try/catch. Instead, you can gently return the error.

// impure
const add = (a, b) => {
    if (typeof a !== 'number' || typeof b !== 'number') {
        throw new Error('add must take two numbers');
    }
    return a + b;
}

// pure
const add = (a, b) => {
    if (typeof a !== 'number' || typeof b !== 'number') {
        return { error: new Error('add must take two numbers') };
    }

    return { result: a + b };
}

You might already use pure functions without knowing it. The important thing to learn here is how to tell a pure from an impure function.

Currying

curry

You know what is even better than a pure function? A function that takes only one argument. It's better because it becomes a simple mapping from a value to another. Such a function can be replaced by an object:

const increment = v => v + 1;
increment(3); // 4
// could be replaced by
const increment = {
    1: 2,
    2: 3,
    3: 4,
};
increment[3]; // 4

Wait, what? How am I supposed to do anything useful with a single argument? I cannot even do a simple add function.

Sure we can: have a function return another function.

function add(a) {
    return function(b) {
        return a + b;
    }
}
// or, using arrow functions
const add = a => b => a + b;

add(3)(5); // 8

This kind of function is called a curried function. The name comes from the mathematician Haskell Curry, who developed the concept (yes, the guy gave his name to this concept, and to a programming language. Badass!). With curried function, code that manipulates other functions know what to expect. A function always needs a single argument to executes.

This also allows for more generic code.

Let's say you need to send a greeting message to a lot of people.

const getMessage => greet => name => `${greet} ${name}`;
const sayHelloTo = getMessage('Hello');

sayHelloTo('world'); // 'Hello world'
sayHelloTo('marmelab'); // 'Hello marmelab'

If you want to be able to call add(a, b), instead of add(a)(b), you can use this uncurry helper function:

const uncurry = fn =>
    (...args) => {
        // traverse the arguments, and for each one
        const result = args.reduce((prevResult, arg) => {
            // as long as the previous result is a function, call it with the argument
            if (typeof prevResult === 'function') {
                return prevResult(arg);
            }
            // otherwise, return the result
            return prevResult;
        }, fn); // initialize the result with the function

        // if the result is a function, we uncurry it too.
        return typeof result === 'function' ? uncurry(result) : result;
    }

const add = unCurry(a => b => c => a + b + c);

add(1)(2)(3); // 6
add(1, 2, 3); // 6
add(1, 2)(3); // 6
add(1)(2, 3); // 6

Additionally, you may want to convert functions with multiple arguments to curried function.

// First we need to know the number of arguments of the function.
// Thankfully, functions have a length parameter that tells us just that.
const curry = (fn, length = fn.length) => (...args) => {
    if (args.length >= length) {
        return fn(...args);
    }

    return curry((...nextArgs) => fn(...args, ...nextArgs), fn.length - args.length);
}

const add = curry((a, b, c) => a + b + c);
add(1)(2)(3); // 6
add(1, 2, 3); // 6
add(1, 2)(3); // 6
add(1)(2, 3); // 6

Note that this curry implementation won't work with functions that take a variable number of arguments, unless you know beforehand how many arguments it will receive. But in this case, why have a function like this in the first place? In the end, it is not possible to curry functions with a variable number of argument.

It will also misbehave with functions that take default parameters.

const mul = (a, b = 2) => a * b;

const badlyCurriedMul = curry(mul);
badlyCurriedMul(1) // 2, the curried function will use the default parameter immediately.

// And that's because mul.length is 1.
console.log(mul.length); // 1
// That's right, function length will not take default parameter into account
// so we need to specify the correct length
const curriedMul = curry(mul, 2);
curriedMul(1); // fn ...
curriedMul(1, 5); // 5
// and now it work

Also, libraries like lodash, or ramda provide a better curry implementation.

Function Composition

composition

One of the great benefits of functions that take only one parameter is composition.

const add5 = a => a + 5;
const double = a => a * 2;
const add5ThenMultiplyBy2 = a => double(add5(a));

add5ThenMultiplyBy2(1) // 12

When functions take only one argument, their calls can get chained (sort of), one taking the result of the other. But writing f1(f2(f3(...))) gets tiresome fast. Thankfully, we can create a helper to compose function calls for us. Let's call it compose.

const compose = (f1, f2) => arg => f1(f2(arg));

const add5ThenMultiplyBy2 = compose(double, add5);
add5ThenMultiplyBy2(1) // 12

With compose, we end up writing the functions in the reverse order of their execution.

The reason for this order comes from mathematics, and is logic. Simply put, if we compose f1 with f2, we get f1(f2(x)), which reads "f1 of f2 of x".

const increment = v => v + 5;
const double = v => v * 2;

const incrementOfDouble = compose(increment, double); // x => increment(double(x));
// It is the composition of increment with double, so we increment the double of x.
incrementOfDouble(5); // 11
// And so 5 get doubled before we increment it,
// even though we passed increment before double to the compose function

Remember: if you have a compose function, the order of the function arguments is their reverse order of execution.

If you want to have a composition that keeps the order of execution (I know I do), you just have to reverse the order f2(f1(x)). In this case, the helper is usually called flow, or sometimes composeRight.

Next Step

Ok, so now we have a unit function that we can compose to get more complex ones. It feels like lego. I love Lego.

But compose takes only two functions arguments. It would be better if it worked with any number of functions. We need a way to generalize our function to work with any number of arguments.

We need the monoid pattern. I will talk about it in the next post of this series. Stay tuned!