Applicative Functors
==================
> {-# LANGUAGE TypeSynonymInstances, FlexibleContexts, NoMonomorphismRestriction, OverlappingInstances, FlexibleInstances #-}
>
> module ApplicativeFunctors where
> import Prelude hiding (sequence)
> import Control.Monad
> import Data.Functor.Compose
> import Test.HUnit
An alternative to monads
========================
We have seen how to use monads to express different kind of
computational behaviors: errors, states, etc. and how to use these
for specific tasks: parsing, evaluators, exception handling, etc.
We have also seen how to combine computational behaviors by means of
monad transformers. In this way, we can build more interesting
examples: evaluators with exceptions, etc.
Monads are not the only components useful to structure
computations. Today we will go back to *functors* and see how a
special class of functors: *applicative functors* represents an
alternative to monads.
Let's take once again expression evaluation as an example:
> -- | A variable
> data Var = A | B | C | D | E | F | G | H | I | J
> deriving (Show)
> -- | Basic information: a literal or undefined
> data Inf = Lit Bool Var | Undef
> deriving (Show)
> data Expr = Or3 Inf Inf Inf
> | Not Expr
> | And Expr Expr
> deriving (Show)
This is a language for Boolean formulas containing atoms that are
3-clause of basic information `Inf`. The basic component `Inf` is either a
literal or an undefined value. An expression containing an undefined
value is partial and it cannot be evaluated to a boolean.
> ok = Not (And (Or3 (Lit True A) (Lit False B) (Lit True C)) (Not (Or3 (Lit True D) (Lit False E) (Lit True F))))
> err= Not (And (Or3 (Lit True A) (Lit False B) (Lit True C)) (Not (Or3 Undef (Lit False E) (Lit True F))))
Suppose once again that we want to evaluate those expressions.
We can do it easily in an unsafe way
> evalInfUnsafe :: Inf -> Bool
> evalInfUnsafe (Lit a b) = a
> evalUnsafe :: Expr -> Bool
> evalUnsafe (Not x) = not (evalUnsafe x)
> evalUnsafe (And x y) = evalUnsafe x && evalUnsafe y
> evalUnsafe (Or3 x y z) = evalInfUnsafe x || evalInfUnsafe y || evalInfUnsafe z
and as expected this produces an exception in the case we evaluate an
expression containing an undefined information `Undef`.
~~~~~{.haskell}
*Main> evalUnsafe ok
*Main> evalUnsafe err
~~~~~
We now know how to take care of undefined behaviors. We can write an
exception handler.
> data Exc a = Raise String
> | Result a
> deriving (Show)
Using `Exc` we can have an evaluator as follow:
> evalInf :: Inf -> Exc Bool
> evalInf (Lit a b) = Result a
> evalInf _ = Raise "undefined value"
> eval :: Expr -> Exc Bool
> eval (Not x) = case eval x of
> Raise s -> Raise s
> Result xb -> Result (not xb)
> eval (And x y) = case eval x of
> Raise s -> Raise s
> Result xb -> case eval y of
> Raise s -> Raise s
> Result yb -> Result (xb && yb)
> eval (Or3 x y z) = case evalInf x of
> Raise s -> Raise s
> Result xb -> case evalInf y of
> Raise s -> Raise s
> Result yb -> case evalInf z of
> Raise s -> Raise s
> Result zb -> Result (xb || yb || zb)
So far so good.
~~~~~{.haskell}
*Main> eval ok
*Main> eval err
~~~~~
Our code is not really modular. We know that one method to better
structure the computation is to use Monads. But how about using
Functor instead?
We need to show that `Exc` can be turned in an instance of `Functor`, but
this is easy!
> instance Functor Exc where
> -- fmap :: Functor f => (a -> b) -> f a -> f b
> fmap f a = case a of
> Raise s -> Raise s
> Result x -> Result (f x)
So, can we use fmap to lift the boolean operations to the `Exc` level?
> eval1:: Expr -> Exc Bool
> eval1 (Not x) = fmap not (eval1 x)
> eval1 (And x y) = fmap2 (&&) (eval1 x ) (eval1 y)
> eval1 (Or3 x y z) = fmap3 (\a -> \b -> \c -> a || b || c)
> (evalInf x) (evalInf y) (evalInf z)
~~~~~{.haskell}
*Main> eval1 ok
*Main> eval1 err
~~~~~
How can we obtain `fmap2` and `fmap3`?
> -- fmap: (a -> b) -> Exc a -> Exc b
> fmap2 :: (a -> b -> c) -> Exc a -> Exc b -> Exc c
> fmap2 f a b = case a of
> Raise s -> Raise s
> Result a -> fmap (f a) b
> fmap3 :: (a -> b -> c -> d) -> Exc a -> Exc b -> Exc c -> Exc d
> fmap3 f a b c = case a of
> Raise s -> Raise s
> Result a -> fmap2 (f a) b c
Suppose that we have an n-ary input function, how can we define the
corresponding `fmapn`?
> -- fmap4 :: (a1 -> a2 -> a3 -> a4 -> r) -> Exc a1 -> Exc a2 -> Exc a3 -> Exc a4 -> Exc r
> -- fmap5 :: ...
> class Functor f => Applicative' f where
> (<**>) :: f (a -> b) -> f a -> f b
> instance Applicative' Exc where
> f <**> a = case f of
> Raise s -> Raise s
> Result f' -> fmap f' a
> fmap2':: (a -> b -> c) -> Exc a -> Exc b -> Exc c
> fmap2' f a b = (fmap f) a <**> b
> fmap3':: (a -> b -> c -> d) -> Exc a -> Exc b -> Exc c -> Exc d
> fmap3' f a b c = (fmap f) a <**> b <**> c
Now, why we need to bring with us `fmap`?
> (<$>) :: Functor f => (a -> b) -> f a -> f b
> (<$>) = fmap
we can now rewrite our example as:
> fmap3'':: (a -> b -> c -> d) -> Exc a -> Exc b -> Exc c -> Exc d
> fmap3'' f a b c = f <$> a <**> b <**> c
Now suppose that our third argument is not an exception but an Integer
on which we cannot fail. We would like to have something like
> -- fmap3''' :: :: (a -> Int -> c -> d) -> Exc a -> Int -> Exc c -> Exc d
How can we obtain it?
We can extend the Applicative' class:
> class Functor f => Applicative f where
> (<*>) :: f (a -> b) -> f a -> f b
> pure :: a -> f a
and we can show that Exc is also an Applicative class
> instance Applicative Exc where
> f <*> a = f <**> a
> pure a = Result a
> fmap3''':: (a -> Int -> c -> d) -> Exc a -> Int -> Exc c -> Exc d
> fmap3''' f a b c = f <$> a <*> pure b <*> c
Finally we can rewrite our evaluator with exception as:
> eval2:: Expr -> Exc Bool
> eval2 (Not x) = not <$> (eval2 x)
> eval2 (And x y) = (&&) <$> (eval2 x ) <*> (eval2 y)
> eval2 (Or3 x y z) = (||) <$> ((||) <$> (evalInf x) <*> (evalInf y)) <*> (evalInf z)
~~~~~{.haskell}
*Main> eval2 ok
*Main> eval2 err
~~~~~
Applicative Functors helps us to structure exceptions. Are they also
useful more in general?
Every Monad is Applicative
==========================
Let's have a look back to Monads. Isn't the pattern of `fmap`, `fmap2`,
`fmap3`, etc familiar?
> -- liftM :: Monad m => (a1 -> r) -> m a1 -> m r
> -- liftM2 :: Monad m => (a1 -> a2 -> r) -> m a1 -> m a2 -> m r
> -- liftM3 :: Monad m => (a1 -> a2 -> a3 -> r) -> m a1 -> m a2 -> m a3 -> m r
Let's try to build them in a modular way:
> liftM' :: Monad m => (a1 -> r) -> m a1 -> m r
> liftM' f a = do a' <- a
> return (f a')
> liftM2' :: Monad m => (a1 -> a2 -> r) -> m a1 -> m a2 -> m r
> liftM2' f a b = do a' <- a
> b' <- b
> return (f a' b')
> liftM3' :: Monad m => (a1 -> a2 -> a3 -> r) -> m a1 -> m a2 -> m a3 -> m r
> liftM3' f a b c = do a' <- a
> b' <- b
> c' <- c
> return (f a' b' c')
How can we make these kind of construction more parametric?
What we want to do is to have a way for
> aP :: Monad m => m (a -> b) -> m a -> m b
> f `aP` x = do f' <- f
> x' <- x
> return (f' x')
With this new operation we can build all the liftMn we need.
> liftM'' :: Monad m => (a1 -> r) -> m a1 -> m r
> liftM'' f x = return f `aP` x
> liftM2'' :: Monad m => (a1 -> a2 -> r) -> m a1 -> m a2 -> m r
> liftM2'' f x y = return f `aP` x `aP` y
> liftM3'' :: Monad m => (a1 -> a2 -> a3 -> r) -> m a1 -> m a2 -> m a3 -> m r
> liftM3'' f x y z = return f `aP` x `aP` y `aP` z
> liftM4'' :: Monad m => (a1 -> a2 -> a3 -> a4 -> r) -> m a1 -> m a2 -> m a3 -> m a4 -> m r
> liftM4'' f x y z w = return f `aP` x `aP` y `aP` z `aP` w
Indeed this shows that every `Monad` is `Applicative`!
Let's have a look to some concrete instances.
> instance Applicative Maybe where
> pure a = return a
> f <*> x = f `aP` x
> instance Applicative IO where
> pure = return
> f <*> x = f `aP` x
More in general we have:
> newtype WrappedMonad m a = Wrap {unWrap :: m a}
> instance Monad m => Functor (WrappedMonad m) where
> fmap = fmap
> instance Monad m => Applicative (WrappedMonad m) where
> pure a = Wrap (return a)
> Wrap f <*> Wrap x = Wrap (liftM2 ($) f x)
But not every Applicative is a Monad. Indeed, we can think that `pure`
can corresponds to `return` but how about `>>=`?
~~~~~{.haskell}
instance Applicative m => Monad m where
return a = pure a
x >>= f = ??
~~~~~
The difference between the two notions can be explained in term of the flow of information
> cond :: Monad m => m Bool -> m a -> m a -> m a
> cond m f g = do bool <- m
> if bool then f else g
> cond' :: Applicative m => m Bool -> m a -> m a -> m a
> cond' m f g = pure (\b t e->if b then t else e) <*> m <*> f <*> g
The evaluation of this expressions can bring to results that are
interestingly different. This because in `cond` the value `bool`
returned by the computation `m` influence the choice of the
result. Conversely, in `cond'` we just sequence the results.
~~~~~{.haskell}
*Main> cond (Just True) (Just False) Nothing
*Main> cond' (Just True) (Just False) Nothing
~~~~~
So, we have that every `Monad` is `Applicative` but not that every
`Applicative` is a `Monad`. This suggests that even if they share many
similarities, `Monad` and `Applicative` are two interesting distinct
concepts. Both of them can be used to structure computation. The
choice on when using one or the other depends on the concrete
application we need to develop.
As a guideline we can think that `Monad` are particularly useful when we
want the value returned by one computation to influence the choice of
another. This is the main characteristic of `>>=`. Conversely,
`Applicative` are useful when the structure of a computation is
fixed and we just need to sequence the results. This is what `<*>` is
useful for.
Applicative Functors Compose Easily
===================================
We have seen how to compose Monads thanks to Monad Transformers. How
about composing Applicative Functors? Clearly, we can define in a
similar way Applicative Transformers (and we have also a library for
doing this). Alternatively we can try to compose them directly.
Let's try to build the product of two Applicative Functors.
> data Prod f g a = Prod (f a) (g a)
> instance (Functor f, Functor g) => Functor (Prod f g) where
> -- fmap: (a -> b) -> Prod f g a -> Prod f g b
> fmap h (Prod x y) = Prod (fmap h x) (fmap h y)
> instance (Applicative f, Applicative g) => Applicative (Prod f g) where
> pure x = Prod (pure x) (pure x)
> Prod f g <*> ~(Prod x y) = Prod (f <*> x) (g <*> y)
Easy, isn't it? but the product is not the real composition. How can we achieve composition then?
First, we need to see how two functors can be composed!
> class Composition cp where
> decompose :: (cp f g) x -> f (g x)
> compose :: f (g x) -> (cp f g) x
-- | Basic functor composition
> newtype CompF f g a = CompF { runCompF :: f (g a) }
> instance Composition CompF where
> compose = CompF
> decompose = runCompF
> instance (Functor f, Functor g) => Functor (CompF f g) where
> fmap f = compose . fmap (fmap f) . decompose
Then the composition of Applicative Functors comes naturally.
> instance (Applicative f, Applicative g) => Applicative (CompF f g) where
> pure x = CompF (pure (pure x))
> CompF f <*> CompF x = CompF (pure (<*>) <*> f <*> x)
An Example of Applicative Functor threading
============================================
Besides composition, Applicative are particularly easy to thread.
Let's go back to the motivating example and see how our evaluator
can be enriched by both exceptions and step counting.
We have defined `Exc`, now we need a step counter. This can be done
easily.
> data Count a = Count a Int
> deriving (Show)
We need to show that `Count` is an Applicative Functor.
> instance Functor Count where
> fmap f (Count a i) = Count (f a) i
> instance Applicative Count where
> pure a = Count a 0
> Count f i <*> Count x j = Count (f x) (i+j)
And we can define the `tick` counter
> tick :: Count a -> Count a
> tick (Count a n) = Count a (n+1)
We can thread `Exc` and `Count` in the evaluation.
> evalInf' :: Inf -> Exc Bool
> evalInf' (Lit a b) = Result a
> evalInf' _ = Raise "undefined value"
> eval3 :: Expr -> Exc (Count Bool)
> eval3 (Not x) = case eval3 x of
> Raise s -> Raise s
> Result (Count xb n) -> Result (Count (not xb) (n+1))
> eval3 (And x y) = case eval3 x of
> Raise s -> Raise s
> Result (Count xb n) -> case eval3 y of
> Raise s -> Raise s
> Result (Count yb m) -> Result (Count (xb && yb) (n+m+1))
> eval3 (Or3 x y z) = case evalInf' x of
> Raise s -> Raise s
> Result xb -> case evalInf' y of
> Raise s -> Raise s
> Result yb -> case evalInf' z of
> Raise s -> Raise s
> Result zb -> Result (Count (xb || yb || zb) 1)
Again, this code is not very modular! Let's use what we have in our
Applicative Functors arsenal. First, we need to generalize to a
generic `Functor f` some of the operations we have introduced above.
> fmap2G :: Applicative f => (a -> b -> c) -> f a -> f b -> f c
> fmap2G f a b = f <$> a <*> b
> fmap3G :: Applicative f => (a -> b -> c -> d) -> f a -> f b -> f c -> f d
> fmap3G f a b c = f <$> a <*> b <*> c
With this we can finally write our evaluator with exception and
step-counting as:
> eval4:: Expr -> Exc (Count Bool)
> eval4 (Not x) = (tick . (fmap not)) <$> (eval4 x)
> eval4 (And x y) = fmap2G ( \s -> \ r -> tick (fmap2G (&&) s r)) (eval4 x) (eval4 y)
> eval4 (Or3 x y z) = fmap3G or3 (evalInf' x) (evalInf' y) (evalInf' z)
> or3 :: Bool -> Bool -> Bool -> Count Bool
> or3 a b c = tick ( fmap2G (||) (fmap2G (||) (pure a) (pure b) ) (pure c) )
~~~~~{.haskell}
*Main> eval4 ok
*Main> eval4 err
~~~~~
Applicative Functor Notation
============================
In the code above we have used some non standard notation. Let's try
to fix what is standard. In the module `Control.Applicative` the
definition of `Applicative` corresponds to the one we gave above:
~~~~~{.haskell}
class Functor f => Applicative f where
(<*>) :: f (a -> b) -> f a -> f b
pure :: a -> f a
~~~~~
The symbol `<*>` is the standard symbol for application of Applicative Functors.
While we used above the symbol `<**>` as a synonym of `<*>`, in the
library it is instead used for a function similar to `<*>` but with the
order of the first two parameters reversed.
~~~~~{.haskell}
<**> :: f a -> f (a -> b) -> f b
~~~~~
Finally, we used the symbol `aP` for the lifted application on Monad. The standard
symbol for that is instead ap
~~~~~{.haskell}
ap :: m (a -> b) -> m a -> m b
~~~~~