Functors, Monads, and Promises
Tags: promise, javascript
Published: 2021-08-29 05:11:09

---
title: Functors, Monads, and Promises
published: true
description: Using ideas from functors and monads to interpret what promises really are
tags: promise,javascript
//cover_image: https://direct_url_to_image.jpg
---

The JavaScript Promise is a tool for asynchronous operation. However, it is a whole lot more powerful than that.

The promise's `then` method can be thought to act both like map and flatMap.

## Arrays, map, flatMap, Functors, and Monads

Recall that in JavaScript arrays, `map` allows you to take an array, and get a totally new array, with each elements entirely transformed. In other words, `map` takes an array (implicitly), a function, and returns another array.

So, for instance, if you wanted to derive an array of strings from an array of numbers, you would invoke the `map` method, by supplying a function.

Here's an example.

```javascript
const nums = [ 1, 2, 3, 4, 5 ];

const strs = nums.map(n => n.toString());

// Should be:
// [ '1', '2', '3', '4', '5' ]
```

Because arrays implement a `map` method, you can think of arrays as functors.

Arrays also implement a `flatMap` method. Like `map`, it is also used to derive an entirely new array. But the key difference here is that rather than the supplied function returning the transformed value, it can return it wrapped inside an array.

```javascript
const nums = [ 1, 2, 3, 4, 5 ];

const strs = nums.flatMap(n => [ n.toString() ]);
// Note: we're returning an ARRAY with a single string!

// Should be:
// [ '1', '2', '3', '4', '5' ]
```

In case your wondering: yes, the returned array can absolutely have more than one element in it. Those values will simply be concatenated into the final result.

Because arrays implement `flatMap`, you can think of arrays as Monads.

## About Functors and Monads

Functors and monads are two constructs that hold value.

Functors implement `map`, and monads implement `flatMap`.

Functors and monads can be defined to hold any number of values, whether it be strictly one, two, three, or unlimited.

## Promises as Functors and Monads

The JavaScript promise represents a construct that holds a single value.

A promise's `then` method acts as both `map`, and `flatMap`.

The method `then`, like `map`, and `flatMap`, will always return a promise.

With `then`, you can have the function return a non-promise value. This will have `then` act like an array's `map` method. Or, you can have that function return a promise. This will have `then` act like an array's `flatMap` method.

Here is `then` acting like `map`.

```javascript
promise.then((x) => {
  return x + 42;
});
```

Here is `then` acting like `flatMap`.

```javascript
promise.then((x) => {
  // Note: Promise.resolve will return a promise.
  return Promise.resolve(x + 42);
});
```

## Monad laws with promise

Monads have [laws](https://en.wikipedia.org/w/index.php?title=Monad_(functional_programming)&oldid=1038071006#Definition). Think of them like Newton's three laws of motion.

These are:

- left-dentity
- right-identity
- associativity

Because promises can be interpreted as monads, you most certainly can use `then` to follow the three laws.

Let's demonstrate. First, let's assume that the functions `f` and `g` accept a value and returns a promise, and p is a promise.

### Left-identity

```javascript
Promise.resolve(x).then(f)

// Is equivalent to

f(x)
```

### Right-identity

```javascript
p.then(Promise.resolve)

// Is equivalent to

p // I'm serious. that's all there is to it.
```

### Associativity

```javascript
p.then(x => f(x).then(g))

// Is equivalent to

p.then(f).then(g)
```

## Monadic error handling in Promise

Traditionally `flatMap` (the `then` in promises) is very instance-specific. After all, you can substitute the name `flatMap` with whatever name you want, so long as the instance behaves like a monad. And in the case of promises, `flatMap` is called `then`.

Other than the name (`then` instead of `flatMap`), the way it is implemented can be different from instance to instance.

And in the case of Promises, it can be implemented so that `then` does not evaluate if the Promise holds no value other than an error.

For example

```javascript
Promise.reject(new Error('Some error'))
  .then(() => {
    console.log('Wee!');
    // Trust me. Nothing will happen here.
  });
```

In order to do anything with the promise, you will need to invoke the `catch` method. The `catch` method will return a promise, just like `then`.

However, while `then` will only evaluate the function if the promise holds a value, `catch` will evaluate the function if the promise holds an error.

```javascript
Promise.reject(new Error('Some error'))
  .then(() => {
    console.log('Wee!');
    // Trust me. Nothing will happen here.

    return Promise.resolve(1);
  })
  .catch(() => {
    console.log('Caught an error!')
    return Promise.resolve(42);
  })
  .then(x => {
    console.log(x);
    // Will log 42, not 1.
  });
```

Interestingly enough, the monad laws will also work with `catch`, as well as `then`.

## Conclusion

So this article went over what a monad is, and how promises can be thought of as monads. To put it in simple terms, an object can be thought of as a monad, as long as it implements some method that looks like `flatMap`.