Functors and Monads in Javascript
Tags: javascript, functional, monads, pratica
Published: 2019-09-12 18:42:41

# Functors and Monads

The purpose of this talk is to shed some light on some of the Functional Programming terms we see thrown about here and there, primarily `Functor` and `Monad`.

What the heck are these? Let's start with 2 phrases that I saw while scouring the internet.

> "A `functor` is something that you can `map`."

> "A `monad` is a `functor` that you can `flatMap`."

Let's dive into it.

## Functors

In mathematics, specifically category theory, a functor is a map between categories.

In mathematics, a category (sometimes called an abstract category to distinguish it from a concrete category) is a collection of "objects" that are linked by "arrows".

Confused? Good.

Simply put a category is a `collection of objects` let's call that something, and a functor is a map between `collections of objects`.

So that brings us to our first statement:

> "A `functor` is something that you can `map`."

Let's look at some code:

```javascript
const collection1 = [1, 2, 3] // [1,2,3]
const collection2 = collection.map(x => x + 1) // [2,3,4]
```

Here we have an array (a collection of Ints). Since we can map collection1 to collection2 by doing `x => x + 1` we can say that Arrays in JS are `Functors`.

Lets say we wanted to create our own functor. This functor will represent a Person Object.

```javascript
const p1 = {
  firstName: 'matthew',
  lastName: 'staniscia',
  hairColor: 'brown',
  age: 37,
}

const Person = value => ({
  value,
})

Person(p1)

/*
Result
{ 
  value:{ 
    firstName: 'matthew',
    lastName: 'staniscia',
    hairColor: 'brown',
    age: 37 
  }
}
*/
```

This is not a functor yet because we can't yet map over it. So lets add a mapping function to it.

```javascript
const p1 = {
  firstName: 'matthew',
  lastName: 'staniscia',
  hairColor: 'brown',
  age: 37,
}

const Person = value => ({
  map: fn => Person(fn(value)),
  value,
})

Person(p1)

/*
Result
{ 
  map: [Function: map],
  value:{ 
    firstName: 'matthew',
    lastName: 'staniscia',
    hairColor: 'brown',
    age: 37 
  }
}
*/
```

We can now map some functions to it.

```javascript
const objectMapper = fn => value =>
  Object.keys(value).reduce((acc, cur, idx, arr) => ({ ...acc, [cur]: fn(value[cur]) }), value)

const makeUpper = s => (typeof s === 'string' ? s.toUpperCase() : s)

Person(p1).map(x => objectMapper(y => makeUpper(y))(x))
Person(p1).map(x => objectMapper(makeUpper)(x))
Person(p1).map(objectMapper(makeUpper))

/*
Result for all 3 calls
{ 
  map: [Function: map],
  value:{ 
    firstName: 'MATTHEW',
    lastName: 'STANISCIA',
    hairColor: 'BROWN',
    age: 37 
  }
}
*/
```

Let's try mapping a couple functions together.

```javascript
const objectMapper = fn => value =>
  Object.keys(value).reduce((acc, cur, idx, arr) => ({ ...acc, [cur]: fn(value[cur]) }), value)

const makeUpper = s => (typeof s === 'string' ? s.toUpperCase() : s)

const checkAge = n => (typeof n === 'number' ? (n <= 35 ? [n, 'You is good.'] : [n, 'You is old.']) : n)

Person(p1)
  .map(objectMapper(makeUpper))
  .map(objectMapper(checkAge))

/*
Result
{ 
  map: [Function: map],
  value:{ 
    firstName: 'MATTHEW',
    lastName: 'STANISCIA',
    hairColor: 'BROWN',
    age: [ 37, 'You is old.' ] 
  }
}
*/
```

This object is now a functor because it is something that we can map. Now it's time to turn it into a Monad.

## Monads

Let's go back to the definition of a Monad from earlier.

> "A `monad` is a `functor` that you can `flatMap`."

### What is flatMap?

In short when you flatMap something, you will run a map function and then flatten it.

In the case of our Person object our output will not look like `Person({...stuff...})` but rather `{...stuff...}`.

We use flatMap to pull out the result of the map from its context. Other names for flatMap are `chain` and `bind`.

Back to code.

```javascript
const Person = value => ({
  map: fn => Person(fn(value)),
  chain: fn => fn(value),
  value,
})
```

Well that looks simple enough. Since we're mapping and taking the value out of context we only need to return the unwrapped value. Let's see it in action.

```javascript
Person(p1).chain(objectMapper(makeUpper))

/*
Result
{ 
  firstName: 'MATTHEW',
  lastName: 'STANISCIA',
  hairColor: 'BROWN',
  age: 37 
}
*/

Person(p1)
  .chain(objectMapper(makeUpper))
  .chain(objectMapper(checkAge))

/* 
Result

TypeError: Person(...).chain(...).chain is not a function
*/
```

Huston, we have a problem. What happening here? Why is it wrong?
It's simple. The return of the first chain is no longer a Person Monad, it's just a JSON string, so trying to chain it again won't work, if we wanted to chain on a chain we need to maintain context.

```javascript
Person(p1)
  .chain(x => Person(objectMapper(makeUpper)(x)))
  .chain(objectMapper(checkAge))

/*
Result
{
  firstName: 'MATTHEW',
  lastName: 'STANISCIA',
  hairColor: 'BROWN',
  age: [ 37, 'You is old.' ]
}
*/
```

But isn't that the same as this?

```javascript
Person(p1)
  .map(objectMapper(makeUpper))
  .chain(objectMapper(checkAge))
```

Yes. Since map keeps context we can map or chain on that context.

I think we've seen something like this before...

## Monad laws

For an object to be a monad is must satisfy 3 monadic laws.

- Left identity
- Right identity
- Associativity

```javascript
// testing monad rules
const x = 'Matt'
const f = x => Person(x)
const g = x => Person(x + ' is kool')

const LI1 = Person(x).chain(f)
const LI2 = f(x)

const RI1 = Person(x).chain(Person)
const RI2 = Person(x)

const AC1 = Person(x)
  .chain(f)
  .chain(g)
const AC2 = Person(x).chain(x => f(x).chain(g))

// Left Identity
// Monad(x).chain(f) === f(x)
// f being a function returning a monad
Object.entries(LI1).toString() === Object.entries(LI2).toString()

// Right Identity
// Monad(x).chain(Monad) === Monad(x)
Object.entries(RI1).toString() === Object.entries(RI2).toString()

// Associativity
// Monad(x).chain(f).chain(g) == Monad(x).chain(x => f(x).chain(g));
// f and g being functions returning a monad
Object.entries(AC1).toString() === Object.entries(AC2).toString()

/*
Result
true
true
true
*/
```

In the case of our Person monad it satisfies these rules.

## Why Use Monads?

You don't need to use Monads. If you do use Monads and write all of your Monads in the same manner, then you'll have a structure that you can chain together and intermix as you want. Monads are pretty much a design structure that can be used to help you track context so that your code is clear and consistent.

Let's take a look at a basic example of different monads being used together. These are very rudimentary monads but they'll get the point across.

We'll create 3 more monads `Child`, `Teen`, and `Adult`. These monads will have some properties that you can access if you want to be able to know if it's a `Child`, `Teen`, or `Adult`.

```javascript
const Person = value => ({
  map: fn => Person(fn(value)),
  chain: fn => fn(value),
  value,
})

const Adult = value => ({
  map: fn => Adult(fn(value)),
  chain: fn => fn(value),
  isChild: false,
  isTeen: false,
  isAdult: true,
  value,
})

const Teen = value => ({
  map: fn => Teen(fn(value)),
  chain: fn => fn(value),
  isChild: false,
  isTeen: true,
  isAdult: false,
  value,
})

const Child = value => ({
  map: fn => Child(fn(value)),
  chain: fn => fn(value),
  isChild: true,
  isTeen: false,
  isAdult: false,
  value,
})
```

We'll also add the functions that we will use to map and / or chain.

```javascript
const objectMapper = fn => value =>
  Object.keys(value).reduce((acc, cur, idx, arr) => ({ ...acc, [cur]: fn(value[cur]) }), value)

const makeUpper = s => (typeof s === 'string' ? s.toUpperCase() : s)

const makeLower = s => (typeof s === 'string' ? s.toLowerCase() : s)

const makeCapitalize = s => (typeof s === 'string' ? s.replace(/(?:^|\s)\S/g, a => a.toUpperCase()) : s)

const addAge = curr => add => curr + add

const setContext = obj => (obj.age < 13 ? Child(obj) : obj.age < 18 ? Teen(obj) : Adult(obj))

const agePerson = age => obj => setContext({ ...obj, age: addAge(obj.age)(age) })
```

Let's start playing with our monads.

```javascript
const p1 = {
  firstName: 'matthew',
  lastName: 'staniscia',
  hairColor: 'brown',
  age: 10,
}

Person(p1).map(objectMapper(makeUpper))

/*
Result: This is a Person Monad
{ 
  map: [Function: map],
  chain: [Function: chain],
  value:
   { 
     firstName: 'MATTHEW',
     lastName: 'STANISCIA',
     hairColor: 'BROWN',
     age: 10 
   }
}
*/


Person(p1)
  .map(objectMapper(makeUpper))
  .chain(setContext)

/*
Result: This is a Child Monad
{ 
  map: [Function: map],
  chain: [Function: chain],
  isChild: true,
  isTeen: false,
  isAdult: false,
  value:
   { 
     firstName: 'MATTHEW',
     lastName: 'STANISCIA',
     hairColor: 'BROWN',
     age: 10 
   }
}
*/


Person(p1)
  .map(objectMapper(makeUpper))
  .chain(setContext)
  .chain(agePerson(4))
  .map(objectMapper(makeLower))

/*
Result: This is a Teen Monad
{ 
  map: [Function: map],
  chain: [Function: chain],
  isChild: false,
  isTeen: true,
  isAdult: false,
  value:
   { 
     firstName: 'matthew',
     lastName: 'staniscia',
     hairColor: 'brown',
     age: 14 
   }
}
*/


Person(p1)
  .map(objectMapper(makeUpper))
  .chain(setContext)
  .chain(agePerson(4))
  .map(objectMapper(makeLower))
  .chain(agePerson(4))
  .map(objectMapper(makeCapitalize))

/*
Result: This is an Adult Monad
{ 
  map: [Function: map],
  chain: [Function: chain],
  isChild: false,
  isTeen: false,
  isAdult: true,
  value:
   { 
     firstName: 'Matthew',
     lastName: 'Staniscia',
     hairColor: 'Brown',
     age: 18 
   }
}
*/
```

Just for fun let’s include another Monad. We’ll use the Maybe monad from the Pratica library and add a function to see if that person can drink in the US.

```javascript
import { Maybe } from 'pratica'

const maybeDrinkInUS = obj => (obj.age && obj.age >= 21 ? Maybe(obj) : Maybe())
```

After running through the pipeline we’ll either return the data structure or a message.

```javascript
Person(p1)
  .map(objectMapper(makeUpper))
  .chain(setContext)
  .chain(agePerson(4))
  .map(objectMapper(makeLower))
  .chain(agePerson(4))
  .map(objectMapper(makeCapitalize))
  .chain(maybeDrinkInUS) // This returns a Maybe Monad
  .cata({
    Just: v => v,
    Nothing: () => 'This Person is too young to drink in the US',
  })

/*
Result
'This Person is too young to drink in the US'
*/

Person(p1)
  .map(objectMapper(makeUpper))
  .chain(setContext)
  .chain(agePerson(4))
  .map(objectMapper(makeLower))
  .chain(agePerson(7)) // Changed this line to now be 21
  .map(objectMapper(makeCapitalize))
  .chain(maybeDrinkInUS) // This returns a Maybe Monad
  .cata({
    Just: v => v,
    Nothing: () => 'This Person is too young to drink in the US',
  })

/*
Result
{ 
  firstName: 'Matthew',
  lastName: 'Staniscia',
  hairColor: 'Brown',
  age: 21 
}
*/
```

## Conclusion

In conclusion a Monad is nothing more than a wrapper/context/class that has ability to:

- Map data within its own context.
- Chain by mapping over its data and extracting it from its context.
- Satisfies the 3 monadic laws.
- It might have extra properties or methods associated to it.

## Sources

The following links helped me understand Monads and be able to put it into words.

1. [https://dev.to/rametta/basic-monads-in-javascript-3el3](https://dev.to/rametta/basic-monads-in-javascript-3el3)
1. [https://www.youtube.com/watch?v=2jp8N6Ha7tY](https://www.youtube.com/watch?v=2jp8N6Ha7tY)
1. [https://medium.com/front-end-weekly/implementing-javascript-functors-and-monads-a87b6a4b4d9a](https://medium.com/front-end-weekly/implementing-javascript-functors-and-monads-a87b6a4b4d9a)
1. [https://medium.com/javascript-scene/javascript-monads-made-simple-7856be57bfe8](https://medium.com/javascript-scene/javascript-monads-made-simple-7856be57bfe8)
1. [https://www.codingame.com/playgrounds/2980/practical-introduction-to-functional-programming-with-js/functors-and-monads](https://www.codingame.com/playgrounds/2980/practical-introduction-to-functional-programming-with-js/functors-and-monads)
1. [https://medium.com/better-programming/tuples-in-javascript-57ede9b1c9d2](https://medium.com/better-programming/tuples-in-javascript-57ede9b1c9d2)
1. [https://hackernoon.com/functional-javascript-functors-monads-and-promises-679ce2ab8abe](https://hackernoon.com/functional-javascript-functors-monads-and-promises-679ce2ab8abe)
1. [https://blog.klipse.tech/javascript/2016/08/31/monads-javascript.html](https://blog.klipse.tech/javascript/2016/08/31/monads-javascript.html)
1. [https://github.com/getify/Functional-Light-JS](https://github.com/getify/Functional-Light-JS)
1. [https://www.youtube.com/watch?v=ZhuHCtR3xq8](https://www.youtube.com/watch?v=ZhuHCtR3xq8)
1. [https://marmelab.com/blog/2018/09/26/functional-programming-3-functor-redone.html](https://marmelab.com/blog/2018/09/26/functional-programming-3-functor-redone.html)
1. [https://www.npmjs.com/package/pratica](https://www.npmjs.com/package/pratica)