Applicative Functors

class Functor_ t => Applicative_ (t :: * -> *) where
    pure :: a -> t a
    apply :: t (a -> b) -> t a -> t b

Several laws (in addition to those for Functor) which we won’t stare at too closely for now.

Maybe

Recall pureMaybe and applyMaybe:

instance Applicative_ Maybe where
    pure :: a -> Maybe a
    pure = Just

    apply :: Maybe (a -> b) -> Maybe a -> Maybe b
    apply (Just f) ma = fmap f ma
    apply _        _  = Nothing

Two Interpretations for Lists

Can think of lists as two different “computational contexts”.

One way is to consider a list as a set values denoting a non-determinstic choice. Under this interpretation, want to apply all functions to all arguments.

instance Applicative_ [] where
    pure :: a -> [a]
    pure a = [a]

    apply :: [(a -> b)] -> [a] -> [b]
 -- apply fs xs = concatMap (\f -> map (\x -> f x) xs) fs
 -- apply fs xs = concatMap (\f -> concatMap (\x -> [f x]) xs) fs
    apply fs xs = [ f x | f <- fs, x <- xs ]

A second way is to consider a list as an ordered sequence of values. Under this interpretation, want to pairwise apply functions to arguments. Because we can only define one instance per type, we need to define a wrapper for (at least) one or the other; the Haskell libraries choose the instance above for “bare” lists and the following wrapper types for the second interpretation, called zip lists.

newtype ZipList a =
  ZipList { getZipList :: [a] }

instance Functor_ ZipList where
    fmap :: (a -> b) -> ZipList a -> ZipList b
    fmap f (ZipList xs) = ZipList (fmap f xs)

instance Applicative_ ZipList where

    apply :: ZipList (a -> b) -> ZipList a -> ZipList b
    apply (ZipList fs) (ZipList xs) = ZipList $ zipWith ($) fs xs

    pure :: a -> ZipList a
    pure f = ZipList $ repeat f

Note: Okay, if you still haven’t seen newtype and newtype (Redux), now really is the time to read it. The library is full of wrapper types to facilitate plugging definitions into various type classes.

Functions

instance Applicative_ ((->) t) where
 -- pure :: a -> ((->) t) a
    pure :: a -> (t -> a)
 -- pure a = \t -> a
    pure   = const

 -- apply :: ((->) t) (a -> b) -> ((->) t) a -> ((->) t) b
    apply :: (t -> a -> b) -> (t -> a) -> (t -> b)
    apply f g = \t -> f t (g t)

An fmap example with functions:

>  pure (^2) `apply` (3*) $ 3   -- same as:  (^2) `fmap` (3*) $ 3 
-- apply (const (^2)) (3*) $ 3
-- (\t1 -> const (^2) t1 ((3*) t1)) $ 3
-- const (^2) 3 ((3*) 3)
-- (\_ -> (^2)) 3 ((3*) 3)
-- (^2) ((3*) 3)
-- (^2) 9
81

An apply example with functions:

>  liftA2 (+) (^2) (3*) 3
-- pure (+) `apply` (^2) `apply` (3*) $ 3
-- const (+) `apply` (^2) `apply` (3*) $ 3
-- apply (apply (const (+)) (^2)) (3*) $ 3
-- (\t1 -> (apply (const (+)) (^2)) t1 ((3*) t1)) $ 3
-- (\t1 -> (\t2 -> (const (+)) t2 ((^2) t2)) t1 ((3*) t1)) $ 3
-- (\t2 -> (const (+)) t2 ((^2) t2)) 3 ((3*) 3)
-- (const (+)) 3 ((^2) 3) ((3*) 3)
-- (\_ -> (+)) 3 ((^2) 3) ((3*) 3)
-- (+) ((^2) 3) ((3*) 3)
-- (+) 9 ((3*) 3)
-- (+) 9 9
18

Another example:

> liftA3 (,,) show (^2) (3*) 3
("3",9,9)

Each of the functions being composed (here, show and (^2) and (3*)) gets to “read” the “environment” value (here 3). We’ll return to this terminology later. For now, let’s rewrite the instance with a different type variable, to suggest an environment:

instance Applicative_ ((->) env) where
    pure :: a -> (env -> a)
    pure a = \env -> a

    apply :: (env -> a -> b) -> (env -> a) -> (env -> b)
    apply f g = \env -> f env (g env)

The Real Applicative

The Applicative class defined in Control.Applicative — and exposed by Prelude — uses the name (<*>) rather than apply. (We alluded to this choice in the “Monad Roadmap”).

class Functor f => Applicative f where
 -- fmap  ::   (a -> b) -> f a -> f b    -- from Functor
    (<*>) :: f (a -> b) -> f a -> f b    -- Applicative_.apply
    pure  :: a -> f a                    -- Applicative_.pure

The Control.Applicative library defines liftA2, liftA3, etc. functions analogous to the lift2Maybe, lift3Maybe, etc. functions we defined before.

Actually, liftA2 is a method inside Applicative (rather than a helper functiond defined outside) and is an alternative to (<*>) for a minimal complete definition.

There are also versions of (<*>) that “keep” the left or right arguments and “ignore” the other:

(<*) :: Applicative f => f a -> f b -> f a
(*>) :: Applicative f => f a -> f b -> f b

Think about how to define these “for free” in terms of (<*>). These may look bizarre, but we’ll see cases in which these operators are quite handy (stay tuned for “parser pipelines”).

Source Files