[Haskell-cafe] On to applicative

Alexander Solla ajs at 2piix.com
Thu Aug 26 04:29:16 EDT 2010


On Aug 26, 2010, at 12:34 AM, michael rice wrote:

> A lot of stuff to get one's head around. Was aware of liftM2,  
> liftM3, etc., but not liftA2, liftA3, etc.
>

liftM and liftA are essentially equivalent (and are both essentially  
equivalent to fmap)  Same for the liftAn = liftMn functions (where n  
is an integer).  Applicative functors are more general than monads, so  
it makes sense for them to have their own functions.  It is a matter  
of history that liftM was defined before liftA.

> So, the statement was true, but not the way that was shown in the  
> example, i.e., with fmap2, fmap3, etc., which required different  
> functions for each of the fmaps.

Strictly speaking, fmap will work with a function in more than one  
argument, as long as it is properly typed.  This is what makes  
applicative functors work.

Consider that a function f :: a -> b -> c also has the type f :: a ->  
(b -> c).   If you feed it an "a", (resulting in a value of the form f  
a), you get a function g :: (b -> c).  In other words, every function  
is a function in one argument.  Some functions just happen to map to  
other functions.

<$> is flip fmap.  f <$> functor = fmap f functor

Consider what happens if f :: a -> b.  (f <$> functor) means "pull an  
a out of the functor, apply f, and return a functor "over" some b.   
That is to say, "lift" f into the functor and apply it.

Now consider what happens if f :: a -> (b -> c).  By analogy, this  
means "pull an a out of the functor object, apply f, and return a  
functor object (f g)  :: f (b -> c)"  (In other words, a functor  
object that "contains" a function g :: b -> c).  In order to get a c  
value out of this, you need to apply g to "something".  But note that  
we're not just dealing with g.  It is "in" the functor already, and so  
doesn't need lifting.  So some smart guy wrote a function called

<*> :: (Functor f) => f (b -> c) -> f b -> f c

that does just that.  This is one of the defining functions for an  
applicative functor.  (And part of the reason for the name.  If the  
functor contains a function, you can "apply the functor" to properly  
typed functor objects.)

The other function is pure :: (a -> b) -> f (a -> b).  It takes a  
function and lifts it into the functor, without applying it to  
anything.  In other words, given an f :: a -> b,

pure f <*> functor = f <$> functor

If f has a bigger type (say, a -> b -> c -> d), you can do things like:

f <$> functor_on_a <*> functor_on_b <*> functor_on_c

Every monad is an applicative functor.  If we have a monad action  
m_f :: m (a -> b), and another one m_a :: (m a), we can get a monad  
action in type (m b) by pulling the function f :: a -> b out of the  
first one and applying it to the b in the second one:

m_f >>= (\f -> liftM f m_a)

or... m_f >>= (flip liftM) m_a

In fact, there is a function called ap :: m (a -> b) -> m a -> m b  
which does just that, and is "essentially equivalent" to <*>.  Of  
course, running return on a function f is equivalent to running pure  
on f.


More information about the Haskell-Cafe mailing list