Monday, February 27, 2012

LYAH Chapter 11b Applicative Functors


Applicative functors
·         Applicative functors are beefed up functors represented by the Applicative typeclass in Control.Applicative.
·         If we have Just 3 and we do fmap (*) (Just 3) we get Just ((*) 3), which can also be written as Just (* 3)
  1. ghci> :t fmap (++) (Just "hey")  
  2. fmap (++) (Just "hey") :: Maybe ([Char] -> [Char])  
  3. ghci> :t fmap compare (Just 'a')  
  4. fmap compare (Just 'a') :: Maybe (Char -> Ordering)  
  5. ghci> :t fmap compare "A LIST OF CHARS"  
  6. fmap compare "A LIST OF CHARS" :: [Char -> Ordering]  
  7. ghci> :t fmap (\x y z -> x + y / z) [3,4,5,6]  
  8. fmap (\x y z -> x + y / z) [3,4,5,6] :: (Fractional a) => [a -> a -> a]  
·         By mapping "multi-parameter" functions over functors, we get functors that contain functions inside them.
  1. ghci> let a = fmap (*) [1,2,3,4]  
  2. ghci> :t a  
  3. a :: [Integer -> Integer]  
  4. ghci> fmap (\f -> 9) a  
  5. [9,18,27,36]  
·         But what if we have a functor value of Just (3 *) and a functor value of Just 5 and we want to take out the function from Just (3 *) and map it over Just 5? With normal functors, we're out of luck, because all they support is just mapping normal functions over existing functors. We can't map a function that's inside a functor over another functor with what fmap offers us.
·         Applicative typeclass (from Control.Applicative) defines two methods, pure and <*>. No default implementations – have to define them both if we want something to be an applicative functor.
  1. class (Functor f) => Applicative f where  
  2.     pure :: a -> f a  
  3.     (<*>) :: f (a -> b) -> f a -> f b  
·         We know that if a type constructor is part of the Applicative typeclass, it's also in Functor, so we can use fmap on it.
·         pure takes a value and wraps it in an applicative functor that has that value as the result inside it.
·         pure takes a value and puts it in some sort of default (or pure) context—a minimal context that still yields that value.
·         <*> is a beefed up fmap. It takes a functor that has a function in it and another functor and sort of extracts that function from the first functor and then maps it over the second one. When I say extract, I actually sort of mean run and then extract, maybe even sequence. We'll see why soon.
·         Let's take a look at the Applicative instance implementation for Maybe.
  1. instance Applicative Maybe where  
  2.     pure = Just  
  3.     Nothing <*> _ = Nothing  
  4.     (Just f) <*> something = fmap f something  
·         <*> extracts the function from the left value if it's a Just and maps it over the right value. If any of the parameters is NothingNothing is the result.
  1. ghci> Just (+3) <*> Just 9  
  2. Just 12  
  3. ghci> pure (+3) <*> Just 10  
  4. Just 13  
  5. ghci> pure (+3) <*> Just 9  
  6. Just 12  
  7. ghci> Just (++"hahah") <*> Nothing  
  8. Nothing  
  9. ghci> Nothing <*> Just "woot"  
  10. Nothing  
·         pure (+3) and Just (+3) is the same in this case.
·         Use pure if you're dealing with Maybe values in an applicative context (i.e. using them with <*>), otherwise stick to Just.
·         Applicative functors allow you to operate on several functors with a single function.
  1. ghci> pure (+) <*> Just 3 <*> Just 5  
  2. Just 8  
  3. ghci> pure (+) <*> Just 3 <*> Nothing  
  4. Nothing  
  5. ghci> pure (+) <*> Nothing <*> Just 5  
  6. Nothing  
·         At first, we have pure (+), which is Just (+). Next, Just (+) <*> Just 3 happens. The result of this is Just (3+). This is because of partial application. Only applying 3 to the + function results in a function that takes one parameter and adds 3 to it. Finally, Just (3+) <*> Just 5 is carried out, which results in a Just 8.
·         Applicative functors and the applicative style of doing pure f <*> x <*> y <*> ... allow us to take a function that expects parameters that aren't necessarily wrapped in functors and use that function to operate on several values that are in functor contexts. The function can take as many parameters as we want, because it's always partially applied step by step between occurrences of <*>.
·         This becomes even more handy and apparent if we consider the fact that pure f <*> x equals fmap f x. This is one of the applicative laws.

<$>
·         Instead of writing pure f <*> x <*> y <*> ..., we can write fmap f x <*> y <*> .... This is why Control.Applicative exports a function called <$>, which is just fmap as an infix operator. Here's how it's defined:
  1. (<$>) :: (Functor f) => (a -> b) -> f a -> f b  
  2. f <$> x = fmap f x  
·         By using <$>, the applicative style really shines, because now if we want to apply a function f between three applicative functors, we can write f <$> x <*> y <*> z. If the parameters weren't applicative functors but normal values, we'd write f x y z.
·         Let's take a closer look at how this works. We have a value of Just "johntra" and a value of Just "volta" and we want to join them into one String inside a Maybe functor. We do this:
  1. ghci> (++) <$> Just "johntra" <*> Just "volta"  
  2. Just "johntravolta"  
·         Before we see how this happens, compare the above line with this:
  1. ghci> (++) "johntra" "volta"  
  2. "johntravolta"  
·         Awesome! To use a normal function on applicative functors, just sprinkle some <$> and <*> about and the function will operate on applicatives and return an applicative. How cool is that?
·         Anyway, when we do (++) <$> Just "johntra" <*> Just "volta", first (++), which has a type of(++) :: [a] -> [a] -> [a] gets mapped over Just "johntra", resulting in a value that's the same asJust ("johntra"++) and has a type of Maybe ([Char] -> [Char]). Notice how the first parameter of (++) got eaten up and how the as turned into Chars. And now Just ("johntra"++) <*> Just "volta" happens, which takes the function out of the Just and maps it over Just "volta", resulting in Just "johntravolta". Had any of the two values been Nothing, the result would have also been Nothing.

Lists
·         Lists (actually the list type constructor, []) are applicative functors. What a surprise! Here's how [] is an instance of Applicative:
  1. instance Applicative [] where  
  2.     pure x = [x]  
  3.     fs <*> xs = [f x | f <- fs, x <- xs]  
·         Earlier, we said that pure takes a value and puts it in a default context. Or in other words, a minimal context that still yields that value. The minimal context for lists would be the empty list, [], but the empty list represents the lack of a value, so it can't hold in itself the value that we used pure on. That's why pure takes a value and puts it in a singleton list. Similarly, the minimal context for the Maybe applicative functor would be a Nothing, but it represents the lack of a value instead of a value, so pure is implemented as Just in the instance implementation for Maybe.
  1. ghci> pure "Hey" :: [String]  
  2. ["Hey"]  
  3. ghci> pure "Hey" :: Maybe String  
  4. Just "Hey"  
·         What about <*>? If we look at what <*>'s type would be if it were limited only to lists, we get (<*>) :: [a -> b] -> [a] -> [b]. It's implemented with a list comprehension<*> has to somehow extract the function out of its left parameter and then map it over the right parameter. But the thing here is that the left list can have zero functions, one function, or several functions inside it. The right list can also hold several values. That's why we use a list comprehension to draw from both lists. We apply every possible function from the left list to every possible value from the right list. The resulting list has every possible combination of applying a function from the left list to a value in the right one.
  1. ghci> [(*0),(+100),(^2)] <*> [1,2,3]  
  2. [0,0,0,101,102,103,1,4,9]  
·         The left list has three functions and the right list has three values, so the resulting list will have nine elements. Every function in the left list is applied to every function in the right one. If we have a list of functions that take two parameters, we can apply those functions between two lists.
  1. ghci> [(+),(*)] <*> [1,2] <*> [3,4]  
  2. [4,5,5,6,3,4,6,8]  
·         Because <*> is left-associative, [(+),(*)] <*> [1,2] happens first, resulting in a list that's the same as[(1+),(2+),(1*),(2*)], because every function on the left gets applied to every value on the right. Then,[(1+),(2+),(1*),(2*)] <*> [3,4] happens, which produces the final result.
·         Using the applicative style with lists is fun! Watch:
  1. ghci> (++) <$> ["ha","heh","hmm"] <*> ["?","!","."]  
  2. ["ha?","ha!","ha.","heh?","heh!","heh.","hmm?","hmm!","hmm."]  
·         Again, see how we used a normal function that takes two strings between two applicative functors of strings just by inserting the appropriate applicative operators.
·         You can view lists as non-deterministic computations. A value like 100 or "what" can be viewed as a deterministic computation that has only one result, whereas a list like [1,2,3] can be viewed as a computation that can't decide on which result it wants to have, so it presents us with all of the possible results. So when you do something like (+) <$> [1,2,3] <*> [4,5,6], you can think of it as adding together two non-deterministic computations with +, only to produce another non-deterministic computation that's even less sure about its result.
·         Using the applicative style on lists is often a good replacement for list comprehensions. In the second chapter, we wanted to see all the possible products of [2,5,10] and [8,10,11], so we did this:
  1. ghci> [ x*y | x <- [2,5,10], y <- [8,10,11]]     
  2. [16,20,22,40,50,55,80,100,110]     
·         We're just drawing from two lists and applying a function between every combination of elements. This can be done in the applicative style as well:
  1. ghci> (*) <$> [2,5,10] <*> [8,10,11]  
  2. [16,20,22,40,50,55,80,100,110]  
·         This seems clearer to me, because it's easier to see that we're just calling * between two non-deterministic computations. If we wanted all possible products of those two lists that are more than 50, we'd just do:
  1. ghci> filter (>50) $ (*) <$> [2,5,10] <*> [8,10,11]  
  2. [55,80,100,110]  
·         It's easy to see how pure f <*> xs equals fmap f xs with lists. pure f is just [f] and [f] <*> xs will apply every function in the left list to every value in the right one, but there's just one function in the left list, so it's like mapping.

IO
·         This is how the instance is implemented:
  1. instance Applicative IO where  
  2.     pure = return  
  3.     a <*> b = do  
  4.         f <- a  
  5.         x <- b  
  6.         return (f x)  
·         Since pure is all about putting a value in a minimal context that still holds it as its result, it makes sense that pure is just return, because return does exactly that; it makes an I/O action that doesn't do anything, it just yields some value as its result, but it doesn't really do any I/O operations like printing to the terminal or reading from a file.
·         If <*> were specialized for IO it would have a type of (<*>) :: IO (a -> b) -> IO a -> IO b. It would take an I/O action that yields a function as its result and another I/O action and create a new I/O action from those two that, when performed, first performs the first one to get the function and then performs the second one to get the value and then it would yield that function applied to the value as its result. We used do syntax to implement it here. Remember, do syntax is about taking several I/O actions and gluing them into one, which is exactly what we do here.
·         With Maybe and [], we could think of <*> as simply extracting a function from its left parameter and then sort of applying it over the right one. With IO, extracting is still in the game, but now we also have a notion of sequencing, because we're taking two I/O actions and we're sequencing, or gluing, them into one. We have to extract the function from the first I/O action, but to extract a result from an I/O action, it has to be performed.
·         Consider this:
  1. myAction :: IO String  
  2. myAction = do  
  3.     a <- getLine  
  4.     b <- getLine  
  5.     return $ a ++ b  
·         This is an I/O action that will prompt the user for two lines and yield as its result those two lines concatenated. We achieved it by gluing together two getLine I/O actions and a return, because we wanted our new glued I/O action to hold the result ofa ++ b. Another way of writing this would be to use the applicative style.
  1. myAction :: IO String  
  2. myAction = (++) <$> getLine <*> getLine  
·         What we were doing before was making an I/O action that applied a function between the results of two other I/O actions, and this is the same thing. Remember, getLine is an I/O action with the type getLine :: IO String. When we use <*> between two applicative functors, the result is an applicative functor, so this all makes sense.
·         If we regress to the box analogy, we can imagine getLine as a box that will go out into the real world and fetch us a string. Doing(++) <$> getLine <*> getLine makes a new, bigger box that sends those two boxes out to fetch lines from the terminal and then presents the concatenation of those two lines as its result.
·         The type of the expression (++) <$> getLine <*> getLine is IO String, which means that this expression is a completely normal I/O action like any other, which also holds a result value inside it, just like other I/O actions. That's why we can do stuff like:
  1. main = do  
  2.     a <- (++) <$> getLine <*> getLine  
  3.     putStrLn $ "The two lines concatenated turn out to be: " ++ a  
·         If you ever find yourself binding some I/O actions to names and then calling some function on them and presenting that as the result by using return, consider using the applicative style because it's arguably a bit more concise and terse.

Functions
·         Another instance of Applicative is (->) r, so functions. They are rarely used with the applicative style outside of code golf, but they're still interesting as applicatives, so let's take a look at how the function instance is implemented.
·         If you're confused about what (->) r means, check out the previous section where we explain how (->) r is a functor.
  1. instance Applicative ((->) r) where  
  2.     pure x = (\_ -> x)  
  3.     f <*> g = \x -> f x (g x)  
·         When we wrap a value into an applicative functor with pure, the result it yields always has to be that value. A minimal default context that still yields that value as a result. That's why in the function instance implementation, pure takes a value and creates a function that ignores its parameter and always returns that value. If we look at the type for pure, but specialized for the (->) rinstance, it's pure :: a -> (r -> a).
  1. ghci> (pure 3"blah"  
  2. 3  
Because of currying, function application is left-associative, so we can omit the parentheses.
  1. ghci> pure 3 "blah"  
  2. 3  
·         The instance implementation for <*> is a bit cryptic, so it's best if we just take a look at how to use functions as applicative functors in the applicative style.
  1. ghci> :t (+) <$> (+3) <*> (*100)  
  2. (+) <$> (+3) <*> (*100) :: (Num a) => a -> a  
  3. ghci> (+) <$> (+3) <*> (*100) $ 5  
  4. 508  
·         Calling <*> with two applicative functors results in an applicative functor, so if we use it on two functions, we get back a function. So what goes on here? When we do (+) <$> (+3) <*> (*100), we're making a function that will use + on the results of (+3)and (*100) and return that. To demonstrate on a real example, when we did (+) <$> (+3) <*> (*100) $ 5, the 5 first got applied to (+3) and (*100), resulting in 8 and 500. Then, + gets called with 8 and 500, resulting in 508.
  1. ghci> (\x y z -> [x,y,z]) <$> (+3) <*> (*2) <*> (/2) $ 5  
  2. [8.0,10.0,2.5]  
·         Same here. We create a function that will call the function\x y z -> [x,y,z] with the eventual results from (+3),(*2) and (/2). The 5 gets fed to each of the three functions and then \x y z -> [x, y, z] gets called with those results.
·         You can think of functions as boxes that contain their eventual results, so doing k <$> f <*> g creates a function that will call k with the eventual results from f and g. When we do something like (+) <$> Just 3 <*> Just 5, we're using+ on values that might or might not be there, which also results in a value that might or might not be there. When we do(+) <$> (+10) <*> (+5), we're using + on the future return values of (+10) and (+5) and the result is also something that will produce a value only when called with a parameter.
·         We don't often use functions as applicatives, but this is still really interesting. It's not very important that you get how the (->) r instance for Applicative works, so don't despair if you're not getting this right now. Try playing with the applicative style and functions to build up an intuition for functions as applicatives.

ZipList (from Control.Applicative)
·         What if we wanted [(+3),(*2)] <*> [1,2] to work in such a way that the first function in the left list gets applied to the first value in the right one, the second function gets applied to the second value, and so on?  I.e. it could result in a list with two values, namely [4,4]. You could look at it as [1 + 3, 2 * 2].
·         Because one type can't have two instances for the same typeclass, the ZipList a type was introduced, which has one constructor ZipList that has just one field, and that field is a list. Here's the instance:
  1. instance Applicative ZipList where  
  2.         pure x = ZipList (repeat x)  
  3.         ZipList fs <*> ZipList xs = ZipList (zipWith (\f x -> f x) fs xs)  
·         <*> does just what we said. It applies the first function to the first value, the second function to the second value, etc. This is done with zipWith (\f x -> f x) fs xs. Because of how zipWith works, the resulting list will be as long as the shorter of the two lists.
·         pure is also interesting here. It takes a value and puts it in a list that just has that value repeating indefinitely. pure "haha"results in ZipList (["haha","haha","haha".... This might be a bit confusing since we said that pure should put a value in a minimal context that still yields that value. And you might be thinking that an infinite list of something is hardly minimal. But it makes sense with zip lists, because it has to produce the value on every position. This also satisfies the law that pure f <*> xs should equal fmap f xs. If pure 3 just returned ZipList [3]pure (*2) <*> ZipList [1,5,10] would result in ZipList [2], because the resulting list of two zipped lists has the length of the shorter of the two. If we zip a finite list with an infinite list, the length of the resulting list will always be equal to the length of the finite list.
·         ZipList a type doesn't have a Show instance, so we have to use the getZipList function to extract a raw list out of a zip list.
  1. ghci> getZipList $ (+) <$> ZipList [1,2,3] <*> ZipList [100,100,100]  
  2. [101,102,103]  
  3. ghci> getZipList $ (+) <$> ZipList [1,2,3] <*> ZipList [100,100..]  
  4. [101,102,103]  
  5. ghci> getZipList $ max <$> ZipList [1,2,3,4,5,3] <*> ZipList [5,3,1,2]  
  6. [5,3,3,4]  
  7. ghci> getZipList $ (,,) <$> ZipList "dog" <*> ZipList "cat" <*> ZipList "rat"  
  8. [('d','c','r'),('o','a','a'),('g','t','t')]  
·         The (,,) function is the same as \x y z -> (x,y,z). Also, the (,) function is the same as \x y -> (x,y).
·         Aside from zipWith, the standard library has functions such as zipWith3zipWith4, all the way up to 7. zipWith takes a function that takes two parameters and zips two lists with it. zipWith3 takes a function that takes three parameters and zips three lists with it, and so on. By using zip lists with an applicative style, we don't have to have a separate zip function for each number of lists that we want to zip together. We just use the applicative style to zip together an arbitrary amount of lists with a function, and that's pretty cool.

liftA2
·         Control.Applicative defines a function that's called liftA2, which has a type of liftA2 :: (Applicative f) => (a -> b -> c) -> f a -> f b -> f c . It's defined like this:
  1. liftA2 :: (Applicative f) => (a -> b -> c) -> f a -> f b -> f c  
  2. liftA2 f a b = f <$> a <*> b  
·         Nothing special, it just applies a function between two applicatives, hiding the applicative style that we've become familiar with. The reason we're looking at it is because it clearly showcases why applicative functors are more powerful than just ordinary functors. With ordinary functors, we can just map functions over one functor. But with applicative functors, we can apply a function between several functors. It's also interesting to look at this function's type as (a -> b -> c) -> (f a -> f b -> f c). When we look at it like this, we can say that liftA2 takes a normal binary function and promotes it to a function that operates on two functors.
·         Here's an interesting concept: we can take two applicative functors and combine them into one applicative functor that has inside it the results of those two applicative functors in a list. For instance, we have Just 3 and Just 4. Let's assume that the second one has a singleton list inside it, because that's really easy to achieve:
  1. ghci> fmap (\x -> [x]) (Just 4)  
  2. Just [4]  
·         OK, so let's say we have Just 3 and Just [4]. How do we get Just [3,4]? Easy.
  1. ghci> liftA2 (:) (Just 3) (Just [4])  
  2. Just [3,4]  
  3. ghci> (:) <$> Just 3 <*> Just [4]  
  4. Just [3,4]  
·         Remember, : is a function that takes an element and a list and returns a new list with that element at the beginning. Now that we have Just [3,4], could we combine that with Just 2 to produce Just [2,3,4]? Of course we could. It seems that we can combine any amount of applicatives into one applicative that has a list of the results of those applicatives inside it.
·         liftA :: Applicative f => (a -> b) -> f a -> f b
·         liftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c
·         liftA3 :: Applicative f => (a -> b -> c -> d) -> f a -> f b -> f c -> f d

sequenceA  Example
·         Let's try implementing a function that takes a list of applicatives and returns an applicative that has a list as its result value. We'll call it sequenceA.
  1. sequenceA :: (Applicative f) => [f a] -> f [a]  
  2. sequenceA [] = pure []  
  3. sequenceA (x:xs) = (:) <$> x <*> sequenceA xs  
·         Ah, recursion! First, we look at the type. It will transform a list of applicatives into an applicative with a list. From that, we can lay some groundwork for an edge condition. If we want to turn an empty list into an applicative with a list of results, well, we just put an empty list in a default context. Now comes the recursion. If we have a list with a head and a tail (remember, x is an applicative and xs is a list of them), we call sequenceA on the tail, which results in an applicative with a list. Then, we just prepend the value inside the applicative x into that applicative with a list, and that's it!
·         So if we do sequenceA [Just 1, Just 2], that's (:) <$> Just 1 <*> sequenceA [Just 2] . That equals(:) <$> Just 1 <*> ((:) <$> Just 2 <*> sequenceA []). Ah! We know that sequenceA [] ends up as being Just [], so this expression is now (:) <$> Just 1 <*> ((:) <$> Just 2 <*> Just []), which is (:) <$> Just 1 <*> Just [2], which is Just [1,2]!
·         Another way to implement sequenceA is with a fold. Remember, pretty much any function where we go over a list element by element and accumulate a result along the way can be implemented with a fold.
  1. sequenceA :: (Applicative f) => [f a] -> f [a]  
  2. sequenceA = foldr (liftA2 (:)) (pure [])  
·         We approach the list from the right and start off with an accumulator value of pure []. We do liftA2 (:) between the accumulator and the last element of the list, which results in an applicative that has a singleton in it. Then we do liftA2 (:) with the now last element and the current accumulator and so on, until we're left with just the accumulator, which holds a list of the results of all the applicatives.
·         Let's give our function a whirl on some applicatives.
  1. ghci> sequenceA [Just 3, Just 2, Just 1]  
  2. Just [3,2,1]  
  3. ghci> sequenceA [Just 3, Nothing, Just 1]  
  4. Nothing  
  5. ghci> sequenceA [(+3),(+2),(+1)] 3  
  6. [6,5,4]  
  7. ghci> sequenceA [[1,2,3],[4,5,6]]  
  8. [[1,4],[1,5],[1,6],[2,4],[2,5],[2,6],[3,4],[3,5],[3,6]]  
  9. ghci> sequenceA [[1,2,3],[4,5,6],[3,4,4],[]]  
  10. []  
·         When used on Maybe values, sequenceA creates a Maybe value with all the results inside it as a list. If one of the values was Nothing, then the result is also a Nothing. This is cool when you have a list of Maybe values and you're interested in the values only if none of them is a Nothing.
·         When used with functions, sequenceA takes a list of functions and returns a function that returns a list. In our example, we made a function that took a number as a parameter and applied it to each function in the list and then returned a list of results. sequenceA [(+3),(+2),(+1)] 3 will call (+3) with 3(+2) with 3 and (+1) with 3 and present all those results as a list.
·         Doing (+) <$> (+3) <*> (*2) will create a function that takes a parameter, feeds it to both (+3) and (*2) and then calls + with those two results. In the same vein, it makes sense that sequenceA [(+3),(*2)] makes a function that takes a parameter and feeds it to all of the functions in the list. Instead of calling + with the results of the functions, a combination of : and pure []is used to gather those results in a list, which is the result of that function.
·         Using sequenceA is cool when we have a list of functions and we want to feed the same input to all of them and then view the list of results. For instance, we have a number and we're wondering whether it satisfies all of the predicates in a list. One way to do that would be like so:
  1. ghci> map (\f -> 7) [(>4),(<10),odd]  
  2. [True,True,True]  
  3. ghci> and $ map (\f -> 7) [(>4),(<10),odd]  
  4. True  
·         Remember, and takes a list of booleans and returns True if they're all True. Another way to achieve the same thing would be with sequenceA:
  1. ghci> sequenceA [(>4),(<10),odd] 7  
  2. [True,True,True]  
  3. ghci> and $ sequenceA [(>4),(<10),odd] 7  
  4. True  
·         sequenceA [(>4),(<10),odd] creates a function that will take a number and feed it to all of the predicates in[(>4),(<10),odd] and return a list of booleans. It turns a list with the type (Num a) => [a -> Bool] into a function with the type (Num a) => a -> [Bool]. Pretty neat, huh?
·         Because lists are homogenous, all the functions in the list have to be functions of the same type, of course. You can't have a list like[ord, (+3)], because ord takes a character and returns a number, whereas (+3) takes a number and returns a number.
·         When used with []sequenceA takes a list of lists and returns a list of lists. Hmm, interesting. It actually creates lists that have all possible combinations of their elements. For illustration, here's the above done with sequenceA and then done with a list comprehension:
  1. ghci> sequenceA [[1,2,3],[4,5,6]]  
  2. [[1,4],[1,5],[1,6],[2,4],[2,5],[2,6],[3,4],[3,5],[3,6]]  
  3. ghci> [[x,y] | x <- [1,2,3], y <- [4,5,6]]  
  4. [[1,4],[1,5],[1,6],[2,4],[2,5],[2,6],[3,4],[3,5],[3,6]]  
  5. ghci> sequenceA [[1,2],[3,4]]  
  6. [[1,3],[1,4],[2,3],[2,4]]  
  7. ghci> [[x,y] | x <- [1,2], y <- [3,4]]  
  8. [[1,3],[1,4],[2,3],[2,4]]  
  9. ghci> sequenceA [[1,2],[3,4],[5,6]]  
  10. [[1,3,5],[1,3,6],[1,4,5],[1,4,6],[2,3,5],[2,3,6],[2,4,5],[2,4,6]]  
  11. ghci> [[x,y,z] | x <- [1,2], y <- [3,4], z <- [5,6]]  
  12. [[1,3,5],[1,3,6],[1,4,5],[1,4,6],[2,3,5],[2,3,6],[2,4,5],[2,4,6]]  
·         This might be a bit hard to grasp, but if you play with it for a while, you'll see how it works. Let's say that we're doingsequenceA [[1,2],[3,4]]. To see how this happens, let's use the sequenceA (x:xs) = (:) <$> x <*> sequenceA xsdefinition of sequenceA and the edge condition sequenceA [] = pure []. You don't have to follow this evaluation, but it might help you if have trouble imagining how sequenceA works on lists of lists, because it can be a bit mind-bending.
·         We start off with sequenceA [[1,2],[3,4]]
·         That evaluates to (:) <$> [1,2] <*> sequenceA [[3,4]]
·         Evaluating the inner sequenceA further, we get (:) <$> [1,2] <*> ((:) <$> [3,4] <*> sequenceA [])
·         We've reached the edge condition, so this is now (:) <$> [1,2] <*> ((:) <$> [3,4] <*> [[]])
·         Now, we evaluate the (:) <$> [3,4] <*> [[]] part, which will use : with every possible value in the left list (possible values are 3 and 4) with every possible value on the right list (only possible value is []), which results in [3:[], 4:[]], which is [[3],[4]]. So now we have (:) <$> [1,2] <*> [[3],[4]]
·         Now, : is used with every possible value from the left list (1 and 2) with every possible value in the right list ([3] and[4]), which results in [1:[3], 1:[4], 2:[3], 2:[4]], which is [[1,3],[1,4],[2,3],[2,4]
·         Doing (+) <$> [1,2] <*> [4,5,6]results in a non-deterministic computation x + y where x takes on every value from[1,2] and y takes on every value from [4,5,6]. We represent that as a list which holds all of the possible results. Similarly, when we do sequence [[1,2],[3,4],[5,6],[7,8]], the result is a non-deterministic computation [x,y,z,w], where x takes on every value from [1,2]y takes on every value from [3,4] and so on. To represent the result of that non-deterministic computation, we use a list, where each element in the list is one possible list. That's why the result is a list of lists.
·         When used with I/O actions, sequenceA is the same thing as sequence! It takes a list of I/O actions and returns an I/O action that will perform each of those actions and have as its result a list of the results of those I/O actions. That's because to turn an[IO a] value into an IO [a] value, to make an I/O action that yields a list of results when performed, all those I/O actions have to be sequenced so that they're then performed one after the other when evaluation is forced. You can't get the result of an I/O action without performing it.
  1. ghci> sequenceA [getLine, getLine, getLine]  
  2. heyh  
  3. ho  
  4. woo  
  5. ["heyh","ho","woo"]  

Applicative Functor Laws
·         Like normal functors, applicative functors come with a few laws. The most important one is the one that we already mentioned, namely that pure f <*> x = fmap f x holds. As an exercise, you can prove this law for some of the applicative functors that we've met in this chapter. The other functor laws are:
·         pure id <*> v = v
·         pure (.) <*> u <*> v <*> w = u <*> (v <*> w)
·         pure f <*> pure x = pure (f x)
·         u <*> pure y = pure ($ y) <*> u
·         We won't go over them in detail right now because that would take up a lot of pages and it would probably be kind of boring, but if you're up to the task, you can take a closer look at them and see if they hold for some of the instances.

Conclusion
·         In conclusion, applicative functors aren't just interesting, they're also useful, because they allow us to combine different computations, such as I/O computations, non-deterministic computations, computations that might have failed, etc. by using the applicative style. Just by using <$> and <*> we can use normal functions to uniformly operate on any number of applicative functors and take advantage of the semantics of each one.


Notes:
·         An applicative functor has more structure than a functor but less than a monad. See the Haddock docs for Control.Applicative.
·         technically, a strong lax monoidal functor
·         lacks the full power of the binding operation >>= but
·         it has more instances.
·         it is sufficient for many uses, e.g. context-free parsing, or the Traversable class.
·          
·         instances can perform analysis of computations before they are executed, and thus produce shared optimizations