That’s a good question! In this response, I’ll show some cases where something looks like a functor (it has a .map(f) method), but did not obey every functor laws.

(Again, I’m going to assume a definition of functor as specified by Haskell and the Fantasy Land specification.)

The ValueMapper

Let’s look at this ValueMapper class.

This is not a functor, because the .map(f) method did not return a wrapper. Although it correctly returns the mapped value, true, but it’s not properly wrapped.

This makes it not chainable, which also violates the second law of functor.

Ruby Hash

In Ruby, an array is a functor, but a hash is not (even though they both provide .map(&f) method).

Let’s define our identity function:

Next, let’s create a hash:

Now, let’s map the identity function over it:

Here, a nested array is returned instead of the same hash, and they are not considered equivalent:

And thus, a Ruby hash is not a functor.

Maybe

In Chapter 8 of Professor Frisby’s Mostly Adequate Guide to Functional Programming, the author presented a Maybe functor, which looks like this (rewritten in ES6).

This Maybe is used to represent two possible states:

  • When there is something (e.g. Maybe.of(42))
  • When there is nothing (e.g. Maybe.nothing())

Now, Maybe.of(null) and Maybe.of(undefined) is considered as Maybe.nothing() (but for good reasons which we will see).

Unfortunately, this Maybe class violates the second functor law, let’s take this data as an example.

I want to get the length of my first name. Living by the Single Responsibility Principle, I create two functions to do it.

Let’s try it by performing two mappings first:

Now let’s try it by composing these two functions first, then perform the mapping:

We get the same thing. This doesn’t seem like a violation of the second functor law…

Hmm…

How about trying it with a different object? Let’s see…

Let’s first perform two mappings:

In case you are wondering why Maybe.nothing() is returned, here’s why: First, I wrapped myData in a Maybe.

Then I take the first name. Since myData does not have the firstName property, I get undefined back, which turns into a Maybe.nothing():

Now, since we now have nothing, there is virtually nothing to map. Hence, we simply get Maybe.nothing() back.

I hope you can see how Maybe can be useful: When a maybe is mapped into null or undefined, it turns into a nothing. It will prevent the lengthOf function to run on undefined value, which can prevent our code from throwing errors. This is a very useful concept, but still, it’s not a functor in the purest sense.

To see why, we’ll map the composed function over that maybe. If Maybe is a functor, then we should also get Maybe.nothing() back…

It throws an error! The second law does not hold anymore, and thus, this Maybe is not a functor.

This happens because Maybe.of(null) and Maybe.of(undefined) has automagically turned into Maybe.nothing(). The shape has changed.

Although the same type, the shape turned from “something” into “nothing.”

In Haskell, a Maybe can hold any value, even () which represents a lack of any value, and it would still consider that ‘something.’

If we make distinction between these three — namely Maybe.of(null), Maybe.of(undefined) and Maybe.nothing() — then this Maybe class wouldn’t be as useful because you wouldn’t be able to chain .map(f) calls and make it not throw an error in case something is mapped to null or undefined along the way.

It’s mappable, but it’s not a real functor.

Still, it works like that for your convenience. We just need to compromise to make up for that billion dollar mistake.

(@dtinth) A software engineer and frontend enthusiast. A Christian. A JavaScript musician. A Ruby and Vim lover.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store