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
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
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)
\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
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
fmap has an infix name
<$>. So finally we get:
f <$> a <*> b <*> c.
Haskell pre-defines a type class
Applicative, which captures the pattern of
<*>. Any type that implements
Applicative works well with
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
pure is equivalent to the default value constructor of
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
is equivalent to
putStrLn "1" *> putStrLn "2"
<* is similar. Both will be reviewed while studying Monad.
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.