Haskell 笔记:Applicative
Motivation
Functor
solves the problem of mapping regular single-parameter functions into a sub-category, but that’s not easy for functions with more than one parameter.
Let’s consider a function with two parameters f :: a -> b -> c
, which can also read as a -> (b -> c)
. Applying fmap
on f
will yield fmap f :: m a -> m (b -> c)
. That’s still distant from what we expect: f' :: m a -> m b -> m c
. To get f'
, we need a transform from m (b -> c)
to m b -> m c
. Here we denote it as <*> :: m (b -> c) -> m b -> m c
. We will later show that such transform is universal for functions with more parameters.
Now consider a function with three parameters f :: a -> b -> c -> d
. We are going to transform it into a wrapped-value version, with the help of fmap
and <*>
.
f :: a -> b -> c -> d
(fmap f) :: m a -> m (b -> (c -> d))
\a_ b_ -> (fmap f a_) <*> b_
:: m a -> m b -> m (c -> d)
\a_ b_ c_ -> ((fmap f a_) <*> b_) <*> c_
:: m a -> m b -> m c -> (m d)
Here \a_ b_ c_ -> ((fmap f a_) <*> b_) <*> c_
is in the desired type. For most of the time, applying parameters directly is actually what we want, instead of the function itself, so the code could simply be written as ((fmap f a) <*> b) <*> c
, where a
, b
and c
are wrapped values. Parenthesis could be omitted if precedences are set properly, which leads to a neat and easy-to-read form:
f `fmap` a <*> b <*> c
In haskell, fmap
has an infix name <$>
. So finally we get: f <$> a <*> b <*> c
.
Applicative
Haskell pre-defines a type class Applicative
, which captures the pattern of <*>
. Any type that implements Applicative
works well with <$>
and <*>
.
class Functor f => Applicative (f :: * -> *) where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
GHC.Base.liftA2 :: (a -> b -> c) -> f a -> f b -> f c
(*>) :: f a -> f b -> f b
(<*) :: f a -> f b -> f a
Note that an Applicative
is also a Functor
. Apart from <*>
, there are some other helper functions or operators in Applicative
.
pure
is equivalent to the default value constructor of f
, e.g. (:[])
for List
or Just
for Maybe
. This may be handful when lifting an unwrapped value to a wrapped one.
liftA2
transforms a binary operator to the corresponding version. The function exists as binary operators would be frequently passed among high-order functions.
*>
takes two wrapped parameters and simply returns the second one, which sequence up two wrapped values. This is quite useful for Applicative
with action semantics, such as IO
. In fact, it’s so useful that Haskell introduces a syntax sugar for it, known as the do-notation
. Particularly:
do
putStrLn "1"
putStrLn "2"
is equivalent to
putStrLn "1" *> putStrLn "2"
<*
is similar. Both will be reviewed while studying Monad.
Author: hsfzxjy.
Link: .
License: CC BY-NC-ND 4.0.
All rights reserved by the author.
Commercial use of this post in any form is NOT permitted.
Non-commercial use of this post should be attributed with this block of text.
OOPS!
A comment box should be right here...But it was gone due to network issues :-(If you want to leave comments, make sure you have access to disqus.com.