Functors for Newbies
This is a tutorial on functors. It is inspired by the alreadyexcellently written article here.
What is a Functor?
In Haskell, there is a Functor type class. If a type is an instance of the Functor class, it is essentially a container type that adds context to any value.
The Functor class is defined as follows:
1 2 

. That looks a bit weird, so I will rewrite it:
1 2 

. So, for a container type to be a real Functor, it has to be able to take in a function, and be able to modify the value contained inside itself without changing the context. This is extremely useful. Let’s look at some functors.
The List Functor
Imagine you have a list with some numbers in it: xs = [1, 2, 3]
. Also imagine you already wrote this function below:
1 2 3 4 

. Now, you’d like to use makeStatement
on the list xs
, which has a type [Int]
, so that every Int
inside the list gets transformed according to makeStatement
. Thankfully, List already has an instance of the Functor class, so we can do it like this:
1 2 

. Though, since List defines fmap
with a shorternamed map
, we can just use map
instead of fmap
to save ourselves a keystroke.^{1} While we’re at it, we might as well drop the xs
because it’s redundant.
1 2 

This is immensely useful. Without the Functor class, you’d have to use pattern matching to manually extract the values contained in the list first before applying functions on them. Imagine a sophisticated, recursive binary tree with millions of elements — as long as the container type has an instance for Functor, you can just use a oneliner fmap
to universally apply the function at hand to every single element inside the tree (and rest assured that the context for all the individual values and their interrelationships remain untouched — a win win!).
For empty lists, You can still pass along a function with fmap
into them, but nothing will happen because they are empty (that is, there are no values inside to modify with the function).
1 

The Maybe Functor
Let’s look at the Maybe type just to prove that they are also functors.
Maybe is a container, except that it can only contain just 1 element (unlike List which can hold multiple elements). Maybe has two constructors: Just
and Nothing
; you can use Just
to put a value into the Maybe type, like how you can use the (:[])
function to put a single item into a list. Alternatively, you can use Nothing
to denote the empty Maybe container (like []
for lists).
1 2 3 4 5 

Again, using fmap
on an empty container will return the empty container asis:
1 

The Tuple Functor?
You might be wondering — what about tuples? Well, because tuples hold different types in a single container, it is impossible to implement fmap
for it. Recall the type signature required for fmap
:
1 2 

. Notice that the container must contain a single type apples
— tuples by their nature have arbitrary numbers of different types (they have apples and oranges in them already). This is the reason why we don’t (and can’t) have an fmap
function for tuples.
Conclusion
Functors are awesome. If you ever define your own custom container type, be sure to make an instance for Functor to make life easy for everyone.
I imagine other types’
fmap
implementation will have longer names, which would encourage you to just use the universal, 4letterfmap
function instead.↩