Skip to Content

Day 77: on Algebras, part 4

Posted on 4 mins read

This post is part 4 of a series on Algebras in Software Development. Here are part 1, part 2 and part 3

You thought we were done with Math? Well, think again. Math is all encompassing and all embracing. I’m not saying we should all go out right now and get all the math books in the library, but it has too bad a rep, mostly undeserved.

This time, the algebra we’ll focus on will be the Functor, another one of those scary words FP folks use all the time. And if I manage to do a good job of it, by the end of this post it won’t be scary anymore.

Patterns

As the Haskellbook puts it, whenever we use an Algebra in our code:

[…] we abstract out a common pattern, make certain it follows some laws, give it an awesome name, and wonder how we ever lived without it.

Now, the part about the name is what makes it kind of scary sometimes, but only because we’re not familiar with them. There’s nothing about a Monoid or a Functor that makes them intrinsically complex, but they are unfamiliar. And unfamiliarity is scary and difficult. This series has been trying to make Monoids feel more familiar so far, and now I’ll strive to do the same for Functors.

Structure

A Functor is all about structure and how to access what that structure holds on the inside, without changing the outside (or even considering it) at all. It prescribes only one operation called fmap that looks like this: fmap :: (a -> b) -> f a -> f b where f is a Functor.

Imagine you have a tiny room that you’re looking at from above. Inside the room, there are 4 tiny brown tables. Now, reach in and paint the tables yellow. The room is your Functor, the tables are your elements inside of it (the a in the type definition) and painting the tables is the function that takes them from being brown tables (a) to being yellow tables (b). The room didn’t change, the walls look the same. Only what was inside changed. That’s what fmap does, and the technical term for it is called lifting.

How to lift

I must admit, I’ve never really quite understood the term lifting, but there’s a mental picture I use to remind myself of what it means. I think of it in terms of layers. In most design software, you have the concept of layers, which is a way to separate different elements into their own, encapsulating world. Well, sometimes you have a layer inside of another layer, nested layers if you will. In order to work on the inner layer, you first have to double click the outer one. And if there’s more nesting, you can keep double clicking your way to the bottom layer. lifting is code for that double click. It’s bringing the layer you’re currently working on, one level up each time.

Functors can be nested like those layers in design software, and fmap is the way you double click your way into whichever level you want to work on.

Law abiding life

There are 2 laws to Functors:

  • Identity ➡ If the function you pass to fmap is the identity function, then fmap id functor === id functor. So, the identity function is a functor’s Identity value, very neat.
  • Composition ➡ fMapping multiple times with different functions will yield the same result as fMapping once with a function that is a composition of the multiple ones (in the same order, of course). This one looks better in code, so bear with me for today, tomorrow will be brighter, I promise.

Now, for a very common Functor and a JS implementation of it:

// Maybe Functor

const Nothing = () => ({
  fmap: fn => Nothing(),
  inspect: () => "Nothing",
  toString: () => "Nothing"
});

const Just = x => ({
  fmap: fn => Just(fn(x)),
  inspect: () => `Just(${x})`,
  toString: () => `Just(${x})`
});

There’s not a lot to this one. We only know that if we have a Just we apply the function to what it’s carrying inside and then re-wrap it in a Just. If we have a Nothing we do, well, nothing. But that gives us a safe way to access the value inside without having to do ugly stuff like if (typeof x !== "undefined"). Isn’t that wonderful? I think it is.

Why do I care?

The same reason why we cared about Monoids. These structures allow us to look at our code from higher up. As long as it’s a Functor, we know we can map over what it’s holding. And that’s powerful.

Tune again tomorrow for more Functors and how we can test laws in Javascript! Exciting times lie ahead 🎉

comments powered by Disqus