What Are They?

A brief introduction to functors, and how they'll tie into this course. (4 min. read)

We'll cover the following
widget

FP defines its own types, and functors are among the most basic. Think of it as a container that holds any value. It can be a string, date, boolean, array, object, etc.

This container must have a map method to be considered a functor.

Here’s a basic example

Press + to interact
const Identity = (x) => ({
value: x,
map: (fn) => Identity(fn(x))
});

This function, Identity, takes a value and returns a functor.

Press + to interact
const name = Identity('Bobo');
console.log(name);

Logging name shows us a functor of this shape

{ value: 'Bobo', map: [Function: map] }

How would you change this functor’s inner value, 'Bobo'?

This is JavaScript, so we could mutate it.

Press + to interact
const name = Identity('Bobo');
name.value = name.value.toUpperCase();
console.log(name);

But we know FP actively discourages this practice, so what if we used map instead?

Press + to interact
const name = Identity('Bobo');
const newName = name
.map(value => value.toUpperCase());
console.log({ name, newName });

Without affecting the previous functor, we’ve created a brand new functor with our desired value!

And since map must return the same type, we can chain it similar to array’s map.

Press + to interact
const name = Identity('Bobo');
const newName = name
.map(value => value.slice(0, 3))
.map(value => `My name is not ${value}!`)
.map(value => value.toUpperCase())
console.log({ name, newName });

This way is immutable, easier to read, and composes better.

And when you need to extract it, just access its .value property.

Summary

  • Functors are containers you can map over.
  • Functors can hold any value
  • map is a composable, immutable way to change a functor’s value.

Who Cares?

Lenses use functors. We’ll be diving into them very soon.