Learn You a Haskell for Great Good! - Miran Lipovaca [119]
ghci> Nothing `mappend` Just "andy"
Just "andy"
ghci> Just LT `mappend` Nothing
Just LT
ghci> Just (Sum 3) `mappend` Just (Sum 4)
Just (Sum {getSum = 7})
This is useful when we’re dealing with monoids as results of computations that may have failed. Because of this instance, we don’t need to check if the computations have failed by seeing if they’re a Nothing or Just value; we can just continue to treat them as normal monoids.
But what if the type of the contents of the Maybe is not an instance of Monoid? Notice that in the previous instance declaration, the only case where we must rely on the contents being monoids is when both parameters of mappend are Just values. When we don’t know if the contents are monoids, we can’t use mappend between them, so what are we to do? Well, one thing we can do is discard the second value and keep the first one. For this purpose, the First a type exists. Here’s its definition:
newtype First a = First { getFirst :: Maybe a }
deriving (Eq, Ord, Read, Show)
We take a Maybe a and wrap it with a newtype. The Monoid instance is as follows:
instance Monoid (First a) where
mempty = First Nothing
First (Just x) `mappend` _ = First (Just x)
First Nothing `mappend` x = x
mempty is just a Nothing wrapped with the First newtype constructor. If mappend’s first parameter is a Just value, we ignore the second one. If the first one is a Nothing, then we present the second parameter as a result, regardless of whether it’s a Just or a Nothing:
ghci> getFirst $ First (Just 'a') `mappend` First (Just 'b')
Just 'a'
ghci> getFirst $ First Nothing `mappend` First (Just 'b')
Just 'b'
ghci> getFirst $ First (Just 'a') `mappend` First Nothing
Just 'a'
First is useful when we have a bunch of Maybe values and we just want to know if any of them is a Just. The mconcat function comes in handy:
ghci> getFirst . mconcat . map First $ [Nothing, Just 9, Just 10]
Just 9
If we want a monoid on Maybe a such that the second parameter is kept if both parameters of mappend are Just values, Data.Monoid provides the Last a type, which works like First a, but the last non-Nothing value is kept when mappending and using mconcat:
ghci> getLast . mconcat . map Last $ [Nothing, Just 9, Just 10]
Just 10
ghci> getLast $ Last (Just "one") `mappend` Last (Just "two")
Just "two"
Folding with Monoids
One of the more interesting ways to put monoids to work is to have them help us define folds over various data structures. So far, we’ve done folds over lists, but lists aren’t the only data structure that can be folded over. We can define folds over almost any data structure. Trees especially lend themselves well to folding.
Because there are so many data structures that work nicely with folds, the Foldable type class was introduced. Much like Functor is for things that can be mapped over, Foldable is for things that can be folded up! It can be found in Data.Foldable, and because it exports functions whose names clash with the ones from the Prelude, it’s best imported qualified (and served with basil):
import qualified Data.Foldable as F
To save ourselves precious keystrokes, we’ve imported it qualified as F.
So what are some of the functions that this type class defines? Well, among them are foldr, foldl, foldr1, and foldl1. Huh? We already know these functions. What’s so new about this? Let’s compare the types of Foldable’s foldr and foldr from Prelude to see how they differ:
ghci> :t foldr
foldr :: (a -> b -> b) -> b -> [a] -> b
ghci> :t F.foldr
F.foldr :: (F.Foldable t) => (a -> b -> b) -> b -> t a -> b
Ah! So whereas foldr takes a list and folds it up, the foldr from Data.Foldable accepts any type that can be folded up, not just lists! As expected, both foldr functions do the same for lists:
ghci> foldr (*) 1 [1,2,3]
6
ghci> F.foldr (*) 1 [1,2,3]
6
Another data structures that support folds is the Maybe we all know and love!
ghci> F.foldl (+) 2 (Just 9)
11
ghci> F.foldr (||) False (Just True)
True
But folding over a Maybe value isn’t terribly