This is part 4 of a 6 part tutorial
Applicative Functors
A Haskell list is also an Applicative Functor. If we want to make MyList
one too, we can
look at the interface for the Applicative class, and implement the right functions.
Interface for the Applicative Typeclass
1 2 3 4 |
|
So, it looks as though we need to implement the functions pure
and <*>
in order to be an
instance of Applicative. This also says that whatever is Applictaive has the prerequisite of
also being a Functor. The function pure
must take any type a
and wrap it in the container MyList.
This is also called lifting a
into the Functor. Implementing pure
is easy enough, because it
is the same thing as our data constructor, Cons
.
The function <*>
has the type signature (<*>) :: f (a -> b) -> f a -> f b
and represents function
application for types that are wrapped in our data structure f
, where the function is also wrapped in
the same data structure f
.
Functions that represent applying functions is sort of weird concept, but I can think of three functions
offhand that do accomplish this in different ways. The three are: $
, <$>
, and <*>
, so let’s look
at their type signatures.
Function Application
1 2 3 4 5 6 |
|
The $
function takes a function (a -> b)
and applies to an a
, not surprisingly
giving you a b
. This is regular function application like odd 3
returning
True. Here I show different ways to use $
, implicitly, explicitly, and infix.
Functon application using $
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
People generally use $
when they want to change the precedence of an expression
without using a lot of parenthesis.
Look at the type signature of <$>
, and the one for fmap
.
Type signatures of <$> and fmap
1 2 3 4 |
|
The function <$>
is fmap
. It is applying a function to the Functor f a
producing f b
. The angle brackets around the $ are indicating that this is
application inside a container. This is called lifting the normal function (a -> b)
into the Functor. What if the function itself is wrapped inside a Functor container?
The answer is that function <*>
is used instead. Why wrap a function in a container to
apply it to some value in the same type of container? Why not just not have
anything in containers at all? Remember that a list is a container and check
this out:
The <*> function
1 2 3 4 5 6 |
|
Since lists are applicative functors, you may combine a list of functions to a list of
values and have it do the obvious thing, apply everything to everything, and then either mconcat
or mappend
the results into a single flat list. This just happens to be how <*>
is implemented for a Haskell
list, because it is really the only way that makes sense to implement it.
Making MyList an Instance of Applicative
1 2 3 4 5 6 7 8 9 10 11 12 |
|
The definition of <*>
is just building on fmap
and mappend
. Which is neat and shows how all
of these things are related. For a list, <*>
just maps each function over each item in the
list like a nested loop, and appends them together into one list.