Applying Functions to Wrapped Values
Unwrapping Values is Tedious
Let's look at our domain of algebraic expressions and the
evalmath function again.
data ME = Num Int | Var Char | Group ME |
          Add ME ME | Sub ME ME | Mul ME ME | Power ME Int | Neg ME
          deriving (Show, Ord, Eq)
The evalmath function evaluates algebraic expressions in
the context of an environment.   
evalmath :: [(Char, Int)] -> ME -> Maybe Int
For example,
*Main> evalmath [('x', 3)] (Add (Var 'x') (Var 'x'))
Just 6
*Main> evalmath [('x', 7), ('y', -1)] (Mul (Add (Var 'x') (Num 2)) (Var 'y'))
Just (-9)
Our evalmath implementation used a recursive program structure,
with pattern matching to process the results.
evalmath env (Num n) = Just n
evalmath env (Var c) = lookup c env
evalmath env (Group g) = evalmath env g
evalmath env (Add f g) = 
	case (evalmath env f, evalmath env g) of 
		(Just x, Just y) -> Just (x + y)
		_ -> Nothing
evalmath env (Sub f g) = 
	case (evalmath env f, evalmath env g) of 
		(Just x, Just y) -> Just (x - y)
		_ -> Nothing
evalmath env (Mul f g) = 
	case (evalmath env f, evalmath env g) of 
		(Just x, Just y) -> Just (x * y)
		_ -> Nothing
evalmath env (Power f n) = 
	case (evalmath env f) of 
		(Just x)  -> Just (x ^ n)
		_ -> Nothing
evalmath env (Neg f) = 
	case (evalmath env f) of 
		(Just x) -> Just (-x)
		_ -> Nothing
Each type we get a result we are checking for a proper result wrapped in Just, or an error signalled by Nothing.
This seems tedious.
Applying fmap
Recall the fmap function which generalizes map for any wrapped structures.
class Functor f where
    fmap :: (a -> b) -> f a -> f b
The Maybe class is an instance of Functor.
instance Functor Maybe where
    fmap f (Just x) = Just (f x)
    fmap f Nothing  = Nothing
For example:
*Main> fmap (0-) (Just 3)
Just (-3)
What fmap allows us to do is to take an ordinary function
and apply it to a wrapped value to get a wrapped result.  This is
shown by the type signature.
*Main> :t fmap (0-) fmap (0-) :: (Num b, Functor f) => f b -> f b
In our case the functor is Maybe.
For our final equation in evalmath, we can now eliminate
the pattern matching, fmap will take care of the 
unwrapping and rewrapping.
evalmath env (Neg f) = fmap (0-) (evalmath env f)
The Applicative Type Class
Can we use fmap for the other equations?   What happens if
we try to use it on the plus (+) function?
*Main> :t fmap (+) fmap (+) :: (Num a, Functor f) => f a -> f (a -> a)
The problem is that fmap will give us a function wrapped in Maybe.
*Main :t fmap(+) (Just 4) fmap(+) (Just 4) :: Num a => Maybe (a -> a)
The <*> Operator (ap)
What we need now is a function to apply a wrapped function to a wrapped value.   Fortunately there is a type class Applicative for this purpose, with an operator <*> that fits the bill.
*Main> import Control.Applicative *Main Control.Applicative> :t (<*>) (<*>) :: Applicative f => f (a -> b) -> f a -> f b *Main Control.Applicative> fmap (+) (Just 4) <*> (Just 3) Just 7
Let's look at the type if we just give the first argument to <+>.
*Main Control.Applicative> :t (fmap (+) (Just 4) <*>) (fmap (+) (Just 4) <*>) :: Num b => Maybe b -> Maybe b
It takes wrapped values to wrapped values, just what is needed. So we can now simplify the equations for the binary operations.
evalmath env (Add f g) = fmap (+) (evalmath env f) <*> (evalmath env g) evalmath env (Sub f g) = fmap (-) (evalmath env f) <*> (evalmath env g) evalmath env (Mul f g) = fmap (*) (evalmath env f) <*> (evalmath env g) evalmath env (Power f n) = fmap (^) (evalmath env f) <*> (Just n)
The liftA2 Function
In essence, we can use fmap directly apply a unary function to a wrapped value, but fmap gives us a wrapped function when we apply it to binary functions.
With <*>, we can then apply a wrapped function to a wrapped value.   But since the case of binary functions is so frequent, there is a function liftA2 that "lifts" to unwrapped values into the applicative class.
*Main Control.Applicative> :t liftA2 liftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c *Main Control.Applicative> liftA2 (+) (Just 4) (Just 8) Just 12
So this allows us to simplify evalmath even further.
evalmath :: [(Char, Int)] -> ME -> Maybe Int
evalmath env (Num n) = Just n
evalmath env (Var c) = lookup c env
evalmath env (Group g) = evalmath env g
evalmath env (Add f g) = liftA2 (+) (evalmath env f) (evalmath env g)
evalmath env (Sub f g) = liftA2 (-) (evalmath env f) (evalmath env g)
evalmath env (Mul f g) = liftA2 (*) (evalmath env f) (evalmath env g)
evalmath env (Power f n) = liftA2 (^) (evalmath env f) (Just n)
evalmath env (Neg f) = fmap (0-) (evalmath env f)