When you’re digging deep into a language or a paradigm, you’ll inevitably find some concepts that are hard to understand.
Not every developer have an academic background so same concepts, terms or abstractions are hard to understand and adopt.
One concept that’s certainly hard to understand is: what is a Functor?
Well, I bet you’re already using functors every day without knowing it!
A Functor is a map between two categories.
Real world example? The awesome .map method from our first article, can be applied on arrays… which are functors!
Let’s throw away all that abstract things and let’s see in practice how to adopt Functors!
Functors in practice
As you can see in the example above, an
array is functor because we can map over it.
We can apply a function to each element and get back the same structure, respecting the Functor Laws:
- Functors must preserve identity morphisms
When performing the mapping operation, if the values in the functor are mapped to themselves, the result will be an unmodified functor.
- Functors preserve composition of morphisms
If two sequential mapping operations are performed one after the other using two functions, the result should be the same as a single mapping operation with one function that is equivalent to applying the first function to the result of the second.
So as you can see, we can apply
add5 functions to our array using the
map method. Chaining multiple
map methods will always produce the same output, thanks to the Functor Laws!
Now you may ask: “as far as I know, .map can be used only on arrays, so only arrays are functors!” …wrong! You can create a Functor out of mostly any value, that’s why is so important to understand what Functors are!
Let’s implement a Functor from zero, respecting the Functor Laws:
Great, we just defined an algebra that will be our starting point!
Now, in order to make it look like a real Functor, we need to make it mappable, always respecting the Functor Laws.
Great! We just created our first mappable Functor! Let’s test it:
That’s awesome! Let’s see if it respects the Functor Laws:
- Does it return an unmodified functor? Yes!
- Are we able to map over and over it, always respecting the first law? Yes!
Functors and Objects
Object can be Functors too! And it’s easiest than you think:
Ok, that may not be so easy if you’re not familiar with immutability. So, I’ll rewrite the same function using
loops to help you understand:
Great! So now that we have our Functor, let’s try it out!
Isn’t that awesome?
You just used classes! Is there a way to make it pure?
So is there a way to build a “pure” Functor?
Holy guacamole that’s sooooo easy! Let’s test it against a number!
Amazing! As you may already know, the following expressions are equivalent:
Why should I care about Functors?
Functors are a key topic in both mathematics and computer science.
Without understanding Functors, we won’t be able to understand Monads and other amazing things.
Functors adds some kind of safety in our program, just think how predictable our output can be.
They are also an awesome abstraction which enables you to work on a certain way on any datatype. Remember
null? Functors makes them safe:
So as you can read, we can only apply certain functions when a value is known (so not
So, in few words, a Functor is a thing we can map over. If you’re familiar with category theory, you may have noticed that a Functor maps from category to category.
A Functor which maps from category back to the same category is called Endofunctor (for instance, a monad is an endofunctor).
I really hope that Functors aren’t a mystery anymore!