Functors in Haskell
18 Jan 2013Introduction
In a previous post, I had lightly grazed the surface of the topic of Functors in Haskell and I thought it was time to come back and be a little more comprehensive about it. Without further ado, let’s talk about Functors.
What is a Functor?
At the code level, a Functor
is any type that is an instance of the Functor typeclass. At a concept level, a Functor
is something that can be mapped over. As soon as anyone says something like this to me I immediately start thinking of any enumerated type (array, list, vector, etc) and this is right.
A Functor only needs to implement 1 function, fmap. How this looks in Haskell is as follows.
Reading this gets to be a mouthful, but bear with me. This definition says: “for my first parameter, I want a function that takes an a
type and returns a b
; for my second parameter, I want an a
value wrapped by this functor and I will return you a b
value wrapped by this functor”. I’ve tried to place emphasis on a
’s and b
’s in that previous sentence, otherwise it reads like rather difficult english. Applying this knowledge to a living-breathing example, I’ll show you the Maybe type.
Looking at this we can see that when a Just
value comes through, it’s the value (wrapped in the Just
) that has the function applied to it. When nothing comes through, we don’t care for the function - we know that the return is going to be Nothing
. The important thing to note here is that it’s the value (wrapped in the Just
) that’s being operated on. Seeing this in action always helps an explanation.
The way that I read this is: “A function” fmap
“that takes a function” (replicate 3)
“and a functor value” (Just 4)
“that then maps that function” (replicate 3)
“over the inner value” (4)
“which gets wrapped up in the functor” (Just)
. This is the best way that I could think of to structure this sentence. It makes sense to me, I hope it helps you also. Again, the important thing to remember is that it’s the inner-value that’s getting processed which is why we ended up with Just [4, 4, 4]
rather than [Just 4, Just 4, Just 4]
. Finally, when you’re implementing functors of your own there are laws that must be met.
Law #1
Mapping the id function over any functor value should produce the functor value that we supplied.
Law #2
Mapping two composed functions over a functor value should be the same as functionally composing those two functions.
This second law makes sense once you think about performing fmap over a function, as it’s just the same as function composition.
This post should arm you sufficiently to start inflicting your own Functors on the world.