Haskell: Functor
Functor
Coming from the world of category theory, a functor is a mapping between two categories:
-
in the world of Haskell, usually you can think of it as a type constructor with some special properties. It takes an object and it wraps it.
-
Functors are the building blocks of most
typeclasses
in Haskell- If that sounds like a bold claim, Have a look here to convince yourself
-
Functors are (by definition) the pipes between categories - you might be using them already without realising
Use case
We use Functors all the time - it is hard to think of a case where you wouldn’t use it.
The Identity Functor
Let’s create the simplest functor possible
data Identity a = Identity a deriving (Eq, Ord, Show)
instance Functor Identity where
fmap f (Identity m) = Identity $ f m
-
TL;DR What does it do?
- Transforms whatever you give to it into the
Identity
type.
- Transforms whatever you give to it into the
-
What does it need to implement to be a Functor?
fmap
aka<$>
with the type signature(a -> b) -> f a -> f b
. It is pretty intuitive this one takes a function froma -> b
and transforms the functorf a
into a functorf b
. Easy.
-
What laws does it need to satisfy?
-
it must preserve the identity morphism
fmap Id = Id
-
it also must preserve composition of morphisms
fmap (f . g) = fmap f . fmap g
-
- A few examples using our Identity functor:
fmap (+3) $ Identity 3 -- 6
(*2) <$> $ Identity 4 -- 8
- Other functor functions:
<$ :: a -> f a -> f b
-
It’s half the fmap to the left (if that makes sense?). You give it an element and then you give it a functor, and it returns the element wrapped in the functor (a bit like it pushes the element out of the functor and replaces it with what is on the left).
-
an example:
5 <$ Identity 6 -- Identity 5
- That is quite neat. You can even use it with higher types for example:
(+1) <$ Identity 5 -- of the type :: Num a => Identity (a -> a)
Do you see what happened? We now have a higher order Identity
type - a
function wrapped in Identity
. But what can we do with it? Well we can apply
it - with fmap
again.
fmap ($5) ((+1) <$ Identity 3) -- Identity 6
-
We basically flipped the roles of the function and application. And of course
($5) :: (a -> b) -> b
is a function that takes a function and it applies to5
. -
Also, if you
import Data.Functor
you get$>
(which is as you guessed the opposite of<$
) andvoid
which is:: f a -> f ()
. Pretty straight forward.
In the end
That was it for today. I will try and make an article on each typeclass
of
Haskell as I am quite enjoying writing these. The next one will probably be on
Applicative Functors
, which you probably know where it leads to.
More reading
These are all very basic applications and simple examples. But if you are curious here is some further reading: