3 ms·
This would be a fun time to talk about the power of `Monoid`s. `Monoid` is a popular Haskell typeclass because the algebraic model for it, the monoid, is simpl
by tel 13y ago
This would be a fun time to talk about the power of `Monoid`s.
`Monoid` is a popular Haskell typeclass because the algebraic model for it, the monoid, is simple, pervasive, and powerful. A monoid is composed of three things: a type, a combiner, and a null value.
Monoid = (T, <>, e)
And it must follow the laws that the null value (`e`) is the zero of the combiner (`<>`) on both sides and that the combiner is associative.
e <> x == x
x <> e == x
x <> y <> z == x <> (y <> z) == (x <> y) <> z
Examples include (Integer, +, 0) or (Integer, *, 1) or (List, append, []).
The trick is that if you are trying to fold up a law-abiding Monoid of any kind then you know two things (a) you have a default value which you can insert wherever you like to no effect and (b) you can reassociate your expression (rotate your evaluation tree) to be any shape you like at all.
((((1 + 1) + 1) + 1 ) + 1) -- left
==
(1 + (1 + (1 + (1 + 1)))) -- right
==
((1 + 1) + (1 + 1) + (1 + 0)) -- parallel
By encoding these laws into the type system, you provide information to the compiler which it could theoretically use to rearrange and optimize your computations on the fly. To my knowledge, this isn't done anywhere yet, but knowledge of those laws does allow you to confidently build efficient parallel algorithms. For instance, we know that a dictionary full of monoidal values is itself a monoid.
instance Monoid v => Monoid (Map k v)
We take the empty dictionary as `e` and let our combiner be the union on keys where values are combined using their own monoidal combiner. This forms a really nice basis for a large class of map-reduce algorithms where the mapping step results in some kind of dictionary-like summarization of values.