An applicative functor is a functor which allows function application inside it.
This is done by providing a mechanism for values (and in Haskell functions are treated identically to values) to be lifted into the context of the applicative functor. There is also a higher-level
fmap to apply functorized functions to functors.
Applicative functors have the following typeclass:
Applicative functors are not in the standard prelude (yet), so must be imported from
Control.Applicative; this module also introduces
<$> which is a synonym for
fmap but in infix form. A lot of code using applicatives uses
<$> instead of
fmap for readability, so for consistency so will I.
Notice the typeclass constraint on an applicative functor: it must first be a functor, which demonstrates what I said before.
<*> (pronounced "ap" as in apply);
pure takes a value and puts it into the context of the applicative functor:
<*> is like a special
<$> which works on functions inside functors:
Applicatives follow the functor laws, and build upon them:
-- Lifting a function into an applicative context and then applying it is the -- same as fmap'ing that function pure f <*> = fmap f -- Hence, identity pure id <*> x = x -- Lifting into applicative context can be deferred - Homomorphism pure f <*> pure x = pure (f x) -- Order of evaluation is interchangable -- LHS means apply x to y in the applicative context -- RHS means evaluate y in the applicative context before applying x to it x <*> pure y = pure ($ y) <*> x -- Composition x <*> (y <*> z) = pure (.) <*> x <*> y <*> z -- By example let x = Just (+1) y = Just (*2) z = Just 3 -- LHS Just (+1) <*> (Just (*2) <*> Just 3) = Just (+1) <*> (Just 6) = Just 7 -- RHS pure (.) <*> Just (+1) <*> Just (*2) <*> Just 3 = Just ((+1) . (*2)) <*> Just 3 = Just 7
Maybe is also an instance of
Applicative, allowing optional functions to be applied to optional values.
The applicative instance for
Maybe looks like this:
Here we pattern match to get a function and a value, applying that function to that value, otherwise we get a
Maybe values are either
Just there or
Nothing is there.
There are actually multiple logical ways about turning lists into applicative functors: one way is to compute every value non-deterministically, and another which is to apply the function at the nth index in the first list to the value at the nth index in the second list.
The first is the default instance of list applicative:
The second instance is called a
ZipList, and is really a type synonym for an ordinary list using the
Try to work out the functor instance of
With this wrapper, the applicative instance looks like this:
instance Applicative ZipList where -- To satisfy the identity law, we turn the second list into an infinite list -- so we aren't left with functions without values to map to pure x = ZipList (repeat x) -- zipWith ($) is zipping the two lists with the function application operator (ZipList fs) <*> (ZipList xs) = ZipList (zipWith ($) fs xs)
Here is an example showing how it works:
Here is the instance for function composition as an applicative:
pure is the same as
const, which takes two functions and throws the second one away (also known as the K combinator):
The effect of this is that it creates a function that returns the value inside it, ignoring its parameter:
<*> here demonstrates the S combinator, applying both functions to the input and applying the output of the first function to the output of the second, which is made clearer by