Function

When I first learned that a function is in fact a functor (I learned it from Learn You a Haskell for Great Good!), it made me confused.

A function is a functor? A functorception?!?

What’s going on here?

Note that we’re talking about pure functions. How can we view a function as a ‘set of values?’

Let’s try by example. Here’s the square function:

const square = (x) => x * x

You can think of this function as an infinite set of all the numbers this function can produce. In this case, it’s a set of non-negative numbers.

For example, the value 4 is in this set, because you can retrieve it by calling square(2) or square(-2). It also holds value 100, because you can get it by calling square(10) or square(-10). NaN is also in this set.

But negative numbers are not in this set, because no matter what number you put into this function, you won’t get a negative number back.

Now, imagine that I took all the numbers inside this set, and add each number by 3 (thereby mapping that function/functor with addThree, another function). What do I get back?

You’ll get a set of numbers not less than 3:

And it’s possible to actually do that in code. We just don’t apply the mapping beforehand; we apply it on-the-fly:

const squareMapped = (x) => addThree(x * x)

Attentive readers will notice that mapping a function over another function is simply function composition:

square.map(addThree) ≡ (addThree ∘ square)

≡ ((x) => addThree(square(x)).

For the sake of example, I’ll be very naughty and just implement Function.prototype.map:

Function.prototype.map = function (f) {

const g = this

return function () {

return f(g.apply(this, arguments))

}

}

Let’s try it:

const squareMapped = square.map(addThree) console.log(squareMapped(2))

// => 7 console.log(squareMapped(10))

// => 103

You see, it seems like I took every possible value that the square function can produce, and added it by three.

Now you see how a function can become a functor.