My last post aimed to make functional programming as straightforward as possible. An explicit goal of that article was to avoid unnecessary jargon like monads and functors and stick to concepts that will make our code better.
This time around let's dive into all the scary terms and make them not so scary. While knowing these terms might not make your code better, they're fun to know.
As a warning, we're gonna be diving into some abstract mathematics. This XKCD is now more relevant than ever.
One final disclaimer, I didn't know any of this when I set out to write this article. I've heavily annotated this post with resources I used to learn this stuff as I was going. If you're already knowledgeable on category theory, please be gentle when correcting me on twitter @MatthewGerstman.
What fun would it be if we didn't start with the titular term, functor.
A functor is anything that can be mapped over. This is most commonly a list, but really it's any object that can be mapped over.
For example, we can make a
Wizard that can be mapped over.
Note: in most practical situations, a functor would be parametric (containing a type parameter like
Wizard does fulfill the basic definition of a functor.
map function also needs to meet the following criteria.
Identity Law 
If map is given an identity function it must return the exact same object. Like so:
functor.map(x => x) === functor
This one is hopefully straightforward.
Composition Law 
functor.map(x => f(g(x))) === functor.map(g).map(f)
Woah that's intimidating. What does this actually mean? Let's look at an example.
learnExpelliarmous. If we call
wizard.map(joinGryffindor).map(learnExpelliarmous) it needs to be equivalent to
wizard.map(w => learnExpelliarmous(joinGryffindor(w))).
Now it's worth calling out, it needs to be functionally equivalent. In abstract mathematics we're not worried about pointers and references so we can consider this good enough. This is also known as "Fast and Loose Reasoning is morally correct." 
The next term we need to dive into is a category. A category is an algebraic structure that models objects and their relationships with each other.
Below we can see a category of int, string, and float.
The category is the entire chart. It's how we convert from int to string, float to string, or int to float, or float to int.
Now while in practice, a functor is just a thing we can map over, it has a more advanced explanation. In category theory, a functor is a transformation between two categories . This is a transformation of the entire category.
For example, we can transform the entire category above with a
List functor. The relationships between all of the types goes from a function like
map(toString). This also applies to
round, and any other functions in our category.
Now that we have functors and categories, let's discuss endofunctors. An endofunctor is a functor that transforms a category into itself.
For all intents and purposes, all functors in programming are endofunctors. A functor is really just metadata around a value that allows us to map over it.
The objects in our category are our types, and the arrows are the relationships between those types. These relationships are fundamental to the language so we can't just create a wrapper (functor) that changes how these types are interrelated.
undefined, and other types, the relationship between them is still defined in the spec and can't be changed with a simple functor.
Practically speaking, all functors in programming are endofunctors. There are all sorts of nuanced exceptions to this rule but those are out of scope here. Think of it like physics 101 when we pretended friction wasn't a thing.
Seriously though, watch that talk, I closed so many tabs after watching that talk.
Cool cool, cool cool cool. We've defined some of the scariest terms in functional programming, let's move on to monoids.
A monoid is a category with one object. A monoid has three important rules: identity, composition, and associativity. In programming, it's a wrapper around an object that enforces these rules.
- Identity: This states that there must be a way to use the monoid such that it returns itself.
- Composition: This states that we must be able to combine values using the monoid.
- Associative: This states that the order of operations remains constant so
(a +b) + c === a + (b + c).
If these seem vague and generalized, well thats how abstract math works. Let's look at a practical example, a string monoid. 
Above, we can see our
StringMonoid with an
identity value and a
We can use the compose function to produce a new string.
If we compose with the identity value we get the same value.
Finally, if we call compose on the same arguments it doesn't matter which pair we do first.
- Adding and multiplying numbers (with 0 and 1 as the identity value respectively).
- Joining multiple arrays.
- Composing multiple functions together.
We did it! We made it to Monads.
Before we dive in let's take a step back. Functors and monoids were both wrappers around a value that allow us to execute operations on them. In the case of functors it was
map in the case of monoids it was
compose, where compose is a single operation.
Now monads. Monad's are both a functor and a monoid. That doesn't make this any simpler though. Let's define this in simpler terms.
A monad is a wrapper around some value that makes it easier to compose functions around it. This is often used to abstract away things like API calls or IO. In fact a
Promise is a monad.
If we look at these types above, we have a
Wizard and a function that returns a
Promise<Wizard>. This promise allows us to compose functions on top of it without worrying about the underlying IO needed to go fetch a
Wizard from the server.
Lets break down further.
A monad is based on a simple symmetry — A way to wrap a value into a context, and a way to unwrap the value from the context .
A monad allows us to lift a type into the monad context. In this case we're going from
Promise<Wizard>. The process of wrapping the
Wizard in a
Promise is called lifting.
A monad also allows us to map from one wrapped type to another. So we can call
wizardPromise.then(joinGryffindor).then(learnExpelliarmous) to update the underlying wizard in the promise.
I should mention that the monad
map is the same
map we get with a functor. This is because monads are a type of functor.
Now it's worth noting that Promises don't explicitly provide a
map function, but
then gets us close enough.
Finally, monads give us a way of going from
Promise<Wizard>. We should be able to have an arbitrarily nested monad and get to the original underlying value. We should also be able to do a
flatMap on it. This is commonly done with a
Back to Monads
Now it's worth noting that Promises don't perfectly map to monads, but they're close enough to understand the data type.
The point here isn't to make our code perfectly mathematical, we're just trying to understand some of the advanced math that powers our languages.
I'm going to admit. This article was challenging to write. I learned a lot in the process. I'd like to reiterate, none of this is important to your day to day code. But if you were curious what all those scary FP terms mean hopefully this satisfied that curiosity.
If you'd like something that will make your code better, check out Functional Programming Fundamentals. You can also check out Map, Filter, Reduce. If you want to learn about lambda calculus check out Mary Had a Little Lambda.
These ads help me pay to keep this site up. Feel free to buy, watch, listen or ignore these like any other ad.
Note: I didn't end up quoting all of these directly, but I consulted all of them while writing this article.