Applicative Functors

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

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

lift id `apply` v = v
lift (.) `apply` u `apply` v `apply` w = u `apply` (v `apply` w)
lift f `apply` lift x = lift (f x)
u `apply` lift y = lift ($ y) `apply` u

Maybe

Recall liftMaybe and applyMaybe:

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

 -- apply :: Maybe (a -> b) -> Maybe a -> Maybe b
    apply (Just f) (Just a) = Just $ f a
    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
 -- lift :: a -> [a]
    lift a = [a]

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

A second way 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
    map f (ZipList xs) = ZipList (Prelude.map f xs)

instance Applicative_ ZipList where

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

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

Functions

instance Applicative_ ((->) t) where
 -- lift :: a -> ((->) t) a
 -- lift :: a -> (t -> a)
 -- lift a = \t -> a
    lift   = 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)

From Applicative_ to Applicative

The Applicative class defined in Control.Applicative (and exposed by Prelude) uses the name pure rather than lift and (<*>) rather than apply.

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_.lift

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

Source Files

results matching ""

    No results matching ""