Conceptually, a monoid is anything that:
- Has a "zero-value" (mempty, e.g., 0)
- Can be "appended" together (mappend, e.g., +)
- Has an identity with the zero-value: x + 0 == x, 0 + x == x
- Is associative: x + y + z == (x + y) + z == x + (y + z)
This isn't limited to just arithmetic with operators like +. You can define whole new types as monoids if they have these characteristics. For example, we can merge together configuration files by using monoid operators (in pseudo-code):
-- Zero Value
mempty == {}
-- Append
{"color": "Blue"} `mappend` {"fontSize": 12} == {"color": "Blue", "fontSize": 12}
-- Identity
{"color": "Blue"} `mappend` {} == {"color": "Blue"}
{} `mappend` {"fontSize": 12} == {"fontSize": 12}
-- Associativity
{} `mappend` {"a": "b"} `mappend` {"c": "d"}
== ({} `mappend` {"a": "b"}) `mappend` {"c": "d"}
== {} `mappend` ({"a": "b"} `mappend` {"c": "d"})
There's no need to break out the really formal definitions if you're just trying to get some nice working code.Borrowing from mathematics is beneficial because we can work with structures that have simple but useful guarantees. In Haskell, you can expect all the above to be true for correct type instances of Monoid, without the implementation details at hand. It's a core tenet of functional programming to disarm complexity with simplicity.