The blog post introduces an important typeclass, Functor, in Haskell.

Functor Typeclass
One of the most important concepts in Haskell that many get confused about is Monad
. It is hard because
many rush too much and try to understand what it is as quickly as possible. Here, I will take a different
approach and go over prerequisites slowly but surely for us to truly understand Monad
. One of the
prerequisites is Functor
. Functor
is a typeclass for type constructors with exactly 1 type that can be
mapped over. A type constructor is a function that takes 1 or more types as parameters to create a
concrete type. The following is how we can create an instance of Functor
for a type constructor f
.
class Functor f where
fmap :: (a -> b) -> f a -> f b
To make a type constructor a valid instance of the Functor
typeclass, we just need to define fmap
,
which can take a function (a -> b)
and apply it to the value of a type inside the type constructor.
This might be hard to understand without a concrete example, so let's see some examples.
Lists
A list is a type constructor that takes a type like Int
, Float
, or String
and generates a new
type [Int]
, [Float]
, and [String]
, which is also a functor or a valid instance of Functor
.
In fact, map
is just a fmap
that works only on lists. Here's how the list is an instance of the Functor
typeclass.
instance Functor [] where
fmap = map
map :: (a -> b) -> [a] -> [b]
That's it. The map
function takes a function and applies it to the elements in a list, which is the same
thing as fmap
. Thus, we can do the following.
ghci> map (*2) [1..3]
[2,4,6]
ghci> fmap (*2) [1..3]
[2,4,6]
ghci> map (++"!") ["Hello", "Nice to meet you"]
["Hello!","Nice to meet you!"]
ghci> fmap (++"!") ["Hello", "Nice to meet you"]
["Hello!","Nice to meet you!"]
Maybe
Another example of a type constructor/functor is Maybe
, which takes a type like Int
, Float
, or String
and generates a new type Maybe Int
, Maybe Float
, and Maybe String
. Here is how Maybe
is an instance of
the Functor
typeclass
instance Functor Maybe where
fmap f (Just x) = Just (f x)
fmap f Nothing = Nothing
We are using pattern matching to define fmap
for different value constructors Just
and Nothing
.
As Nothing
has null, we want Nothing
as an output, while we want to apply a function f
to a value inside a Just
.
This means we can use the fmap
function on Maybe
to perform the following.
ghci> fmap (*2) (Just 4)
Just 8
ghci> fmap (*2) Nothing
Nothing
ghci> fmap (++"!") (Just "Hello")
"Hello!"
ghci> fmap (++"!") Nothing
Nothing
IO
IO
is actually a type constructor/functor as well (IO ()
, IO String
, IO Float
, etc.) that has a corresponding fmap
function.
instance Functor IO where
fmap f action = do
result <- action
return (f result)
It binds a value out of an IO action
to result
, applies the function f
to result
, and creates an IO action with that value using return
.
Hence, we can do something like the following.
ghci> fmap (++"!") getLine
Hello
"Hello!"
ADVANCED CHALLENGE: (->) r
is also a valid instance of Functor
. Try coming up with how its fmap
is defined. (Hint: ->
is used in type definition
like r -> a
, and it is also a type constructor that take two types as parameters to create a new type for a function. )
Exercises
This is an exercise section where you can test your understanding of the material introduced in the article. I highly recommend solving these questions by yourself after reading the main part of the article. You can click on each question to see its answer.
Resources
- NA. Making Our Own Types and Typeclasses. Learn You a Haskell for Great Good.
- NA. Functors, Applicative Functors and Monoids. Learn You a Haskell for Great Good.