/ Javascript

Functional Javascript: Functors

Yeah!! Functors, applicatives, monads. More and more these terms are popping up around the Javascript world, and for good reason! Functional programming is a fascinating paradigm which is both an enjoyable intellectual endeavor and a practical way to improve your code quality.

However, many people are initially scared off from this fun and fruitful adventure because with it comes some rather daunting terminology. I mean what the heck is a functor? Truth is, its nothing too fancy but the definitions of these terms are not readily deducible.

So in this series I aim to remove the fear and explain some of these terms in plain old english.

Definition

An item/object whose value can be mapped to a new value through a function

Yup, that's the essence of a functor. You can go to the wikipedia definition and fall into a rabbit hole of category theory definitions but this is enough to get us started. (Though I would encourage you to give some Saturdays to category theory. I'm digging it, slowly, anyway.)

Now before we write some code we need to slightly redefine what it means to map over a value. More than likely when you read the block above some thing like this comes to mind:

const newArray = someArray.map((val, i) => val*i);

While the array is a functor, its value is being transformed via a function, it does not give us a whole picture. What if this were executable code?

const letter = "a";
const capitalLetter = letter.map((l) => l.toUpperCase());
console.log(capitalLetter); // A

As an aside, you actually can do this by using Array.prototype.map.call(string, fn). But, as written this would not run. I'd imagine it's not to hard to conceptualize the operation though right? If we just put const letter = ["a"] instead we immediately see how that would work. What about this?

const num = 5;
const incNum = num.map((n) => n+1);
console.log(incNum); // 6

Again this won't run(not even with .call()). But again not to hard to conceptualize the process that we might expect if this was valid. And it is this conception that is critical to understanding functors. Functors are not collections of values which will be mapped. Functors are a value, be it a number, a letter, a string, an array, an object or any other. Map takes a function and transforms a value into a new value.

Obviously to make this happen in Javascript in we are going to need a new map. But how is that going to work? How is map going to apply a function transformation to data irregardless of type? That's where the functor comes in. Let's refocus the definition from earlier.

A functor is an interface which contains a value and implements a process of mapping its value through a function

In Action

Now let's look at some code. We know we need an interface and in Javascript that generally means an object.

const _Identity = function(val) {
  this.val = val;
};
const identity = new _Identity("Hi");
console.log(identity); // { val: "Hi" }

Standard object constructor, so what?

_Identity.prototype.map = function(fn) {
  return new _Identity(fn(this.val));
}

Boom! _Identity just became a functor. More important than the term, we now have a simple constructor which allows us to map a transform over ANY value type! Now, I don't like having to call new all over the place, lets clean it up a little.

const Identity = (val) => new _Identity(val);

const map = (fn, obj) => obj.map(fn(obj.val)) );

// Or curry it and make it even better!
const map = _.curry((fn, obj) => obj.map(fn(obj.val) ));

There you have it we now have a nice simple API: Identity(<val>) and map(<fn>, <functor>). With map curried we now have tremendous power to create a language that describes the actions in our program in a clean and understandable way. And this language can be used to apply action to any data type, not just arrays.

When you break it down a functor is nothing but a wrapper around any arbitrary value that implements map for that value. I think it's worth pointing out two things about this. One, not scary! Really a functor is a very understandable thing with an awesome power.

Two, a rebutle to the inevitable next question: "Why have names like functor if it can be described so simply?". The reason is that it still took me 14 words to describe it in this minimal form, which absolutely misses many nuances. The terms exist because they allow us humans to discuss these concepts and categories of things in a succinct way. The same reason for all other nouns.