Haskell Language Applicative Functor Common instances of Applicative

Help us to keep this website almost Ad Free! It takes only 10 seconds of your time:
> Step 1: Go view our video on YouTube: EF Core Bulk Extensions
> Step 2: And Like the video. BONUS: You can also share it!

Example

Maybe

Maybe is an applicative functor containing a possibly-absent value.

instance Applicative Maybe where
    pure = Just
    
    Just f <*> Just x = Just $ f x
    _ <*> _ = Nothing

pure lifts the given value into Maybe by applying Just to it. The (<*>) function applies a function wrapped in a Maybe to a value in a Maybe. If both the function and the value are present (constructed with Just), the function is applied to the value and the wrapped result is returned. If either is missing, the computation can't proceed and Nothing is returned instead.

Lists

One way for lists to fit the type signature <*> :: [a -> b] -> [a] -> [b] is to take the two lists' Cartesian product, pairing up each element of the first list with each element of the second one:

fs <*> xs = [f x | f <- fs, x <- xs]
         -- = do { f <- fs; x <- xs; return (f x) }

pure x = [x]

This is usually interpreted as emulating nondeterminism, with a list of values standing for a nondeterministic value whose possible values range over that list; so a combination of two nondeterministic values ranges over all possible combinations of the values in the two lists:

ghci> [(+1),(+2)] <*> [3,30,300]
[4,31,301,5,32,302]

Infinite streams and zip-lists

There's a class of Applicatives which "zip" their two inputs together. One simple example is that of infinite streams:

data Stream a = Stream { headS :: a, tailS :: Stream a }

Stream's Applicative instance applies a stream of functions to a stream of arguments point-wise, pairing up the values in the two streams by position. pure returns a constant stream – an infinite list of a single fixed value:

instance Applicative Stream where
    pure x = let s = Stream x s in s
    Stream f fs <*> Stream x xs = Stream (f x) (fs <*> xs)

Lists too admit a "zippy" Applicative instance, for which there exists the ZipList newtype:

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

instance Applicative ZipList where
    ZipList xs <*> ZipList ys = ZipList $ zipWith ($) xs ys

Since zip trims its result according to the shortest input, the only implementation of pure that satisfies the Applicative laws is one which returns an infinite list:

    pure a = ZipList (repeat a)   -- ZipList (fix (a:)) = ZipList [a,a,a,a,...

For example:

ghci> getZipList $ ZipList [(+1),(+2)] <*> ZipList [3,30,300]
[4,32]

The two possibilities remind us of the outer and the inner product, similar to multiplying a 1-column (n x 1) matrix with a 1-row (1 x m) one in the first case, getting the n x m matrix as a result (but flattened); or multiplying a 1-row and a 1-column matrices (but without the summing up) in the second case.

Functions

When specialised to functions (->) r, the type signatures of pure and <*> match those of the K and S combinators, respectively:

pure :: a -> (r -> a)
<*> :: (r -> (a -> b)) -> (r -> a) -> (r -> b)

pure must be const, and <*> takes a pair of functions and applies them each to a fixed argument, applying the two results:

instance Applicative ((->) r) where
    pure = const
    f <*> g = \x -> f x (g x)

Functions are the prototypical "zippy" applicative. For example, since infinite streams are isomorphic to (->) Nat, ...

-- | Index into a stream
to :: Stream a -> (Nat -> a)
to (Stream x xs) Zero = x
to (Stream x xs) (Suc n) = to xs n

-- | List all the return values of the function in order
from :: (Nat -> a) -> Stream a
from f = from' Zero
    where from' n = Stream (f n) (from' (Suc n))

... representing streams in a higher-order way produces the zippy Applicative instance automatically.



Got any Haskell Language Question?