Applicative functors, Part II

CIS 194 Week 11
1 April 2012

Suggested reading:

We begin with a review of the Functor and Applicative type classes:

class Functor f where
  fmap :: (a -> b) -> f a -> f b

class Functor f => Applicative f where
  pure  :: a -> f a
  (<*>) :: f (a -> b) -> f a -> f b

Every Applicative is also a Functor—so can we implement fmap in terms of pure and (<*>)? Let’s try!

fmap g x = pure g <*> x

Well, that has the right type at least! However, it’s not hard to imagine making Functor and Applicative instances for some type such that this equality does not hold. Since this would be a fairly dubious situation, we stipulate as a law that this equality must hold—this is a formal way of stating that the Functor and Applicative instances for a given type must “play nicely together”.

Now, let’s see a few more examples of Applicative instances.

More Applicative Examples

Lists

How about an instance of Applicative for lists? There are actually two possible instances: one that matches up the list of functions and list of arguments elementwise (that is, it “zips” them together), and one that combines functions and arguments in all possible ways.

First, let’s write the instance that does all possible combinations. (For reasons that will become clear next week, this is the default instance.) From this point of view, lists represent nondeterminism: that is, a value of type [a] can be thought of as a single value with multiple possibilities. Then (<*>) corresponds to nondeterministic function application—that is, the application of a nondeterministic function to a nondeterministic argument.

instance Applicative [] where
  pure a        = [a]          -- a "deterministic" value
  [] <*> _      = []
  (f:fs) <*> as = (map f as) ++ (fs <*> as)

Here’s an example:

names  = ["Joe", "Sara", "Mae"]
phones = ["555-5555", "123-456-7890", "555-4321"]

employees1 = Employee <