[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