Learn You a Haskell for Great Good! - Miran Lipovaca [121]
64800
foldMap isn’t useful only for making new instances of Foldable. It also comes in handy for reducing our structure to a single monoid value. For instance, if we want to know if any number in our tree is equal to 3, we can do this:
ghci> getAny $ F.foldMap (\x -> Any $ x == 3) testTree
True
Here, \x -> Any $ x == 3 is a function that takes a number and returns a monoid value: a Bool wrapped in Any. foldMap applies this function to every element in our tree and then reduces the resulting monoids into a single monoid with mappend. Suppose we do this:
ghci> getAny $ F.foldMap (\x -> Any $ x > 15) testTree
False
All of the nodes in our tree will hold the value Any False after having the function in the lambda applied to them. But to end up True, mappend for Any must have at least one True value as a parameter. That’s why the final result is False, which makes sense because no value in our tree is greater than 15.
We can also easily turn our tree into a list by doing a foldMap with the \x -> [x] function. By first projecting that function onto our tree, each element becomes a singleton list. The mappend action that takes place between all those singleton lists results in a single list that holds all of the elements that are in our tree:
ghci> F.foldMap (\x -> [x]) testTree
[1,3,6,5,8,9,10]
What’s cool is that all of these tricks aren’t limited to trees. They work on any instance of Foldable!
Chapter 13. A Fistful of Monads
When we first talked about functors in Chapter 7, you saw that they are a useful concept for values that can be mapped over. Then, in Chapter 11, we took that concept one step further with applicative functors, which allow us to view values of certain data types as values with contexts and use normal functions on those values while preserving the meaning of those contexts.
In this chapter, you’ll learn about monads, which are just beefed-up applicative functors, much like applicative functors are beefed-up functors.
Upgrading Our Applicative Functors
When we started off with functors, you saw that it’s possible to map functions over various data types using the Functor type class. The introduction to functors had us asking the question, “When we have a function of type a -> b and some data type f a, how do we map that function over the data type to end up with f b?” You saw how to map something over a Maybe a, a list [a], an IO a, and so on. You even saw how to map a function a -> b over other functions of type r -> a to get functions of type r -> b. To answer the question of how to map a function over some data type, all we needed to do was look at the type of fmap:
fmap :: (Functor f) => (a -> b) -> f a -> f b
And then we just needed to make it work for our data type by writing the appropriate Functor instance.
Then you saw a possible improvement of functors and had a few more questions. What if that function a -> b is already wrapped inside a functor value? Say we have Just (*3)—how do we apply that to Just 5? What if we don’t want to apply it to Just 5, but to a Nothing instead? Or if we have [(*2),(+4)], how do we apply that to [1,2,3]? How could that even work? For this, the Applicative type class was introduced:
(<*>) :: (Applicative f) => f (a -> b) -> f a -> f b
You also saw that you can take a normal value and wrap it inside a data type. For instance, we can take a 1 and wrap it so that it becomes a Just 1. Or we can make it into a [1]. It could even become an I/O action that does nothing and just yields 1. The function that does this is called pure.
An applicative value can be seen as a value with an added context—a fancy value, to put it in technical terms. For instance, the character 'a' is just a normal character, whereas Just 'a' has some added context. Instead of a Char, we have a Maybe Char, which tells us that its value might be a character, but it could also be an absence of a character. The Applicative type class allows us to use normal functions on these values with context, and that context is preserved. Observe an example:
ghci> (*)