The term monoid is a bit aggressive and intimidating. In practice, though, it’s actually a very simple concept with very powerful applications not only in mathematics but in application code and programming language design. To begin, let’s define another stealthy concept — a Semigroup.
Semigroups are more of a recent concept relative to its monoid sibling. Both are algebraic structures. An algebraic structure represents a set of possible elements (e.g the characters in a string or the positive natural numbers) with one or more operations (functions) that abide by certain laws (axioms).
In the case of a semigroup, there is a set of elements with only one binary operation that closes over or restricts the elements contained in the said group (hence, “semi”); this operation by contract must be associative. Like we learned in algebra, associative means that you can group the operations different ways and the result is the same:
Monoids are semigroups.
As such they are algebraic structures with a Set and a single binary associative (additive) operation (concat). In addition, its set has a single identity element (also known as empty or neutral element) with the following laws. For any monoid M:
m.concat(M.empty()) leaves m unchanged (right identity)
M.empty().concat(m) leaves m unchanged (left identity)
We’ll be proving this law as well as associativity. In other words, monoids are commutative with respect to concat and its neutral element, irrespective of what they are.
A mixin is a way to extend an object’s prototype with some arbitrary behavior — think traits.
Note: In this case, “functional” has nothing to do with functional programming; rather it means to literally use a function to mix-in the behavior and get around some limitations with object mixins using Object.assign.
What about async?
As you saw earlier, functions are monoidal over composition, so whether they are asynchronous is beside the point. All we need to do is make sure that we use an asynchronous abstraction that is also monoidal under some form of “concatenation.” How do we concatenate events separated in time? “Do this then do that…” How about promises? If asynchronous functions are what we want, let’s implement the Chain monoid around promises and composition.
As identity I choice a promise that resolves right-away. Just so that we don’t mess with the Function.prototype, consider this simple wrapper.
Similar to the synchronous case, the order I combine these doesn’t matter, the result is the same:
That’s quite impressive because now I don’t have to really worry about the timing of these functions or deeply nested callbacks, they are still executing in the same manner.
In fact, this monoid pattern has penetrated deeply into the design of Promise. Coming back to the relationship between associativity and concurrency I alluded to earlier, you can see that reflected here:
Last but not least: Fold
More specifically, a fold takes two parameters: an accumulator callback function and an initial value. Hence, we can make a direct parallel to monoids as naturally foldable with concat as the accumulator and empty as the initial value. And because this is the contract that all monoids abide by, we can generalize a single fold function to work with all of them!
This will work in all cases with elements of type M, irrespective of what M is because reduced only relies on the monoid contract.
And it doesn’t take a ninja to walk the path of the monoid!
To conclude, remember a monoid is an algebraic structure with just an additive operation with an identity object. While very simple in structure, it’s used is immensely powerful and has inspired lots of really great API designs over the years.
Welcome to WordPress. This is your first post. Edit or delete it, then start writing!