1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
|
# Lecture 7: Folds and Monoids
## Folds, again
We've seen folds, how to use them, and how to implement them as well. However, so far we've only seen them working on lists. Not all data structures are lists, though! What if we needed to fold a tree? Although this section focuses on trees, the principles are appliable to data structures of any form.
First of all, the definition of `Tree` and `leaf`:
data Tree a = Empty
| Node (Tree a) a (Tree a)
deriving (Show, Eq)
leaf :: a -> Tree a
leaf x = Node Empty x Empty
Our starting point is going to be an ad hoc function to compute the size of a tree. In other words, we're going to calculate how many elements the tree has.
treeSize :: Tree a -> Integer
treeSize Empty = 0
treeSize (Node l _ r) = 1 + treeSize l + treeSize r
What about flattening a tree into a list?
treeFlatten :: Tree a -> [a]
treeFlatten Empty = []
treeFlatten (Node l x r) = flatten l ++ [x] ++ flatten r
Using just these two function definitions we can spot their differences and use this information to generalize and create a generic fold function for trees. What changes between them? We're going to create our fold for trees step by step below, by comparing each line of the two functions.
Let's start with the first line of these two functions, specifically their type signature: both functions take a parameter of the same type (a generic `Tree`), which is the main subject of the computation, but they return entirely different things depending on what the computation itself actually does. We could say that, although they start at the same point, they travel and end up in substantially different places. This means that our generalized fold is going to have a `Tree` as one of its parameters, but the final return type is unknown. Note that the type of `Tree` and the return type are independent!
treeFold :: Tree a -> b
Now, let's go on with the second line of these functions. Both do pattern matching on the `Empty` constructor and return something. However, since their final return type is unknown and depends on the computation, like we just said, this value is going to be specified as another parameter in the generic fold's type signature. It can't be otherwise!
treeFold :: b -> Tree a -> b
treeFold e Empty = e
Finally, let's take a look at the third line. This is the case where the `Tree` is non-empty: the heart of our computation! In this part, the two functions behave completely differently. Since their behavior depends on the parameters of the `Node` constructor, it makes sense to replace such behavior with a function, rather than a value. After all those expressions *do* something, rather than *being* something. What is the type signature of this function going to be? The same as the `Node` constructor: a sub-tree, followed by a value, followed by another sub-tree.
treeFold :: b -> (b -> a -> b -> b) -> Tree a -> b
treeFold e _ Empty = e
treeFold e f (Node l x r) = f (treeFold e f l) x (treeFold e f r)
If we rewrite `treeSize` and `treeFlatten` with our brand-new `treeFold`, we get:
treeSize' :: Tree a -> Integer
treeSize' = treeFold 0 (\l _ r -> 1 + l + r)
treeFlatten' :: Tree a -> [a]
treeFlatten' = treeFold [] (\l x r -> l ++ [x] ++ r)
### Folds in general
The reason why we just implemented a fold for trees is to state a fact: folds are not necessarily restricted to lists, but they can be applied to many more data structures.
In general, a fold function for any given type `T` takes one argument for each of `T`'s constructors. Each of these arguments is a function that indicates how to translate the respective constructor and combine its data into an expression suitable for the final result. Many functions are expressible as simple folds, that's why it's important to grasp its concept.
## Monoids
Monoids are one of the standard type classes. We can find its definition in the `Data.Monoid` module. Monoids are defined as follows.
class Monoid m where
mempty :: m
mappend :: m -> m -> m
mconcat :: [m] -> m
mconcat = foldr mappend mempty
(<>) :: Monoid m => m -> m -> m
(<>) = mappend
As we can see, instances of `Monoid` define an element called `mempty` and a function called `mappend`, the latter equivalently named `(<>)`. `mempty` and `mappend` have two important properties: `mappend` satisfies associativity, and `mempty` is the identity value for `mappend`.
Associativity means that, in an expression or sub-expression made of `mappend` operations only, regardless of how we arrange their arguments and associate (i.e. parenthesize) the operations, the final result is going to be the same.
In addition to that, identity means that `mempty` is a special value: if given as one of the two arguments of an `mappend` function, the function returns its other argument. Obviously, if the both arguments are the identity, the result is going to be the identity again.
`mconcat` is just a shorthand for combining a list of values. Although its default implementation uses `foldr`, there might be more efficient ways of implementing it depending on the particular instance of `Monoid`.
An example monoid for lists, as the function names suggest, would be:
instance Monoid [a] where
mempty = []
mappend = (++)
And this would be all about monoids, but what if we had two possible monoids for the same type? For example, both addition and multiplication are suitable monoids for integers, but we can't create more than one instance of a type class for the same type.
We can solve this issue by creating a new type for each instance we want to create:
newtype Sum a = Sum a
deriving (Eq, Ord, Num, Show)
getSum :: Sum a -> a
getSum (Sum a) = a
instance Num a => Monoid (Sum a) where
mempty = Sum 0
mappend = (+)
newtype Product a = Product a
deriving (Eq, Ord, Num, Show)
getProduct :: Product a -> a
getProduct (Product a) = a
instance Num a => Monoid (Product a) where
mempty = Product 1
mappend = (*)
### Answers to the two final challenges
It's possible to make an instance of `Monoid` for `Bool`, but there are as many instances as boolean operators: one for AND, one for OR, one for XOR, etc. The reason is that, like integers, boolean values have different associative functions, each with its own identity value.
Instances of `Monoid` for function types can be defined like this:
instance Monoid (a -> a) where
mempty = id
mappend = .
|