Recently I started writing about functional programing in Javascript by talking about what a functor is. Essentialy a functor is:

An object with a map method that can be applied to the values it contains

In this post I used the identity functor as an example because it is extremely simple but I wanted to write about a more practical functor, the maybe.


So isn't one of the beautiful things about functional programming that it is deterministic? Why would we need something called a maybe? It sounds very unsure of itself. Well the fact that FP is deterministic refers to the fact that for a given input a function has only one output. But we can't guarantee what the input is! Looking back at the Identity functor and the map that I used in the last post, this code makes sense and is being used as intended:

const divBy2 = (num) => num/2;
map(divBy2, map(divBy2, Identity(8))); // Gives us 2

But what happens when instead I try and map inc over a string?

map(divBy2, map(divBy2, Identity('It blows up!'))); //NaN

In this case would we get a program crashing error, no. But how many times have you had to rewrite the logic to check if a value was defined before operating on it? Probably plenty. And certainly we have all run have all seen undefined is not a function at one point in time. Well that's what Maybe solves for us. Maybe is a built in validity check. Let's look.

const _Maybe = function(val) {
  this.val = val;

const Maybe = (val) => new _Maybe(val);

We can set it up and wrap it in a convenient constructor exactly like we did with the identity function. The difference comes from the implementation of map which Maybe has. = function(fn) {
  return this.val ? Maybe(fn(this.val)) : Maybe(null);

This particular implementation just checks that this.val for is truthy but we could have it use whatever we wanted for the Maybe. The point is that when we use map on a maybe it now automatically checks for a valid value on the object and then either applies the function or returns us a null Maybe! So now when we try and do something that might actually blow up with an invalid value our program has a way to catch this gracefully and continue.

const reverse = compose(map(reverse), Maybe);

reverse("string"); // Maybe("gnirts")
reverse(undefined); // Maybe(null)