Jean-Francois Paradis

Web Developer + Architect

Monads in 10 Minutes

| Comments

In computer science, a Monad is a design pattern, and like all design patterns, its goal is to solve a problem with simplicity.

Use Case

Let’s define an object containing one value, and see how we can manipulate that value:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Definition
function Simple (value) {

    this.getValue = function () {
        return value;
    }

    this.setValue = function (newValue) {
        value = newValue;
    }
}

// Initialization
var simple = new Simple(10);

// Manipulation
var value = simple.getValue();
value = Math.log(value);
simple.setValue(value);

That’s a lot of code to write every time we need to manipulate the object. To solve that problem we could add a method:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Definition
function Simple (value) {

    this.getValue = function () {
        return value;
    }

    this.setValue = function (newValue) {
        value = newValue;
    }

    this.calculateLog = function() {
        value = Math.log(value);
    }
}

// Initialization
var simple = new Simple(10);

// Manipulation
simple.calculateLog();

This is better, but it means that we have to keep adding methods every time we need a new calculation.

Using Simple Monads

Think of a Monad as the opposite of a function: a function contains code and takes a value for argument; a Monad contains a value and take code for argument. Both of the following are equivalent:

1
2
Math.log(10); // 2.3026
new Monad(10).bind(Math.log).return(); // 2.3026

The constructor wraps an initial value:

1
2
3
var Monad = function(value) {
    this.value = value;
}

The bind() method applies a function to the value and returns the Monad for chaining:

1
2
3
4
Monad.prototype.bind = function(fn) {
    this.value = typeof fn === 'function' ? fn.call(this, this.value) : NaN;
    return this;
}

The return() method unwraps the value:

1
2
3
Monad.prototype.return = function() {
    return this.value;
}

Monads allow you to execute multiple arbitrary functions in a sequence:

1
2
3
4
5
6
new Monad(10)
    .bind(Math.log)
    .bind(function(value) {
        return value > 2 ? Math.floor(value) : value;
    })
    .return(); // 2

The 3 Monadic Laws

There are various types of Monads, but their definition is beyond the scope of this article. What is important to know is that all Monads obey a few axioms.

For our proofs below, let’s define a few constants:

1
2
3
4
var a = Math.PI, // a constant
    f = Math.floor, // a function
    g = Math.log, // a function
    h = Math.sqrt; // a function

Left Identity

return() is a neutral function. Applying return() to a Monad returns its value:

1
new Monad(a).bind(f).return() === f(a); // true

Right Identity

Binding return() to a Monad doesn’t alter the Monad:

1
new Monad(a).bind(f).bind(Monad.prototype.return).return() === f(a);  // true

Associativity

Binding is independent of the grouping of elements. Binding two functions in succession is equivalent to binding one function determined from them:

1
2
3
4
5
new Monad(a).bind(f).bind(g).bind(h).return() === new Monad(a).bind(f).bind(
    function(x) {
        return new Monad(x).bind(g).bind(h).return();
    }
).return(); // true

Comments