|
| |||
|
|
Monoids and co-monoids The writer monad, `W a = (a, w)` usually assumes a monoid as its fixed type `w`. The reader monad, `R a = r -> a`, does not assume any properties for its fixed type `r`. I realized that this asymmetry is an illusion. In fact, the reader monad assumes a co-monoid for `r`. A type `w` is a monoid if there exist two methods, `unit: 1 -> w` and `combine: (w, w) -> w`. The `combine` operation must be associative. The unit must be the identity element for the `combine` operation. Inverting the arrow directions, we obtain the definition of co-monoid: A type `r` is a co-monoid if there exist two methods, `counit: r -> 1` and `expand: r -> (r, r)`. The `expand` operation must be co-associative. The co-associativity means that fst (expand (snd (expand r))) = snd( expand (fst (expand r))). In other words, `expand` is a function product made of two functions, `expand1 : r -> r` and `expand2 : r -> r`. Co-associativity means that these two functions must commute. Note that the `counit` always exists and even is unique because `1` is the terminal object in the category, which always exists as far as functional programming is concerned. So we can just omit `counit` from the definition of the co-monoid. The result is the definition of co-semigroup. So, co-semigroups and co-monoids are the same thing for the purposes of functional programming. The reader monad is defined via the `join` method: `join: (r -> (r -> a)) -> r -> a` `join rra = r -> rra r r` To implement `join`, we need two copies of `r`. Clearly, we are tacitly using the `expand` operation here. The trivial definition `expand r = (r, r)` is a co-monoid for any type r. For this reason, we did not notice that a co-monoid is actually being used. Making this explicit, we find the definition of `join` like this: `join rra = r -> (uncurry rra) (expand r)` Now, the writer monad can be weakened to the writer pre-monad, by omitting the `unit` operation and reducing the monoid W to a semigroup. No such weakening is necessary for the reader monad, because co-monoids and co-semigroups are the same. We have seen that any type `r` is trivially a co-monoid. What are nontrivial examples of generic co-monoid types? I found two examples so far. 1. Consider the triple `(t, t, t)` of the same type `t`. The `expand` operation will copy the middle value towards the middle: expand (x, y, z) = ((x, y, y), (y, y, z)) Co-associativity is achieved because any composition of `expand` will always yield a structure of the form (x, y, y), (y, y, y), ..., (y, y, y), (y, y, z). The two functions are `expand1 (x,y,z) = (x, y, y)` and `expand2 (x,y,z) = (y, y, z)`. The composition of `expand1` and `expand2` in any order gives `(y, y, y)`, which shows co-associativity of `expand`. 2. Consider a list type `List t` and define `expand` as follows: expand [] = ([], []) expand [x] = ([], []) expand [x, ..., y] = ([x, ...], [..., y]) The composition of `expand1` and `expand2` gives the middle piece `...` from the initial list, or the empty list if it's too short. These examples are similar because they operate on the "accordion" principle: the data type has a "middle piece" that is being expanded by copying. Are there other examples of nontrivial co-monoids that operate in a different way? |
||||||||||||||