3 ms·
Monoids are pretty cool in theory, and at the same time, the tool I use least of when working with Haskell. This page [0] really shows how elegant Monoid compr
by mhitza 5y ago
Monoids are pretty cool in theory, and at the same time, the tool I use least of when working with Haskell.
This page [0] really shows how elegant Monoid comprehension would be when implementing SQL-like operations within pure Haskell code. (Goes over desugaring, implementation details and comparison to list/monad comprehensions, just jump down to examples)
[0] https://lptk.github.io/programming/2018/10/04/comprehending-monoids-with-class.html https://lptk.github.io/programming/2018/10/04/comprehending-...
- chas 5y agoFunny enough, monoids/semigroups are one of my favorite parts of Haskell and I really miss having an explicit abstraction for them in other languages. A lot of the code I write is looping over all of the values of a data structure to either look for a certain condition being true (any/all boolean monoid), look for an extremal value (min/max semigroup), or combine all of the values in the data structure (sum/product/average monoid). The product of a monoid is also a monoid, so if you want to do two or more of those operations at once, the code remains simple and orthogonal while only traversing the structure once. In particular, all of the those operations are nicely expressed as one-lines using foldMap (https://hackage.haskell.org/package/base-4.9.1.0/docs/Data-Foldable.html#v:foldMap https://hackage.haskell.org/package/base-4.9.1.0/docs/Data-F...). If you have a data structure called `xs` and each element in data structure has an integer field called `size` you can do the following by using foldMap with different Monoid instances. Sum the sizes: let (Sum total_size) = foldMap (\x -> Sum (size x)) xs Check if any size is greater than 5: let (Any over_threshold) = foldMap (\x -> Any ((size x) > 5)) xs Do both in one pass: let (Sum total_size, Any over_threshold) = foldMap (\x -> (Sum (size x), Any ((size x) > 5))) xs Get the first entry with a size greater than 5: let (First over_threshold) = foldMap (\x -> First (if (size x) > 5 then Just x else Nothing)) xs Get all entries with a size greater than 5: let over_threshold = foldMap (\x -> if (size x) > 5 then [x] else []) xs Since all of these operations are associative, we can completely change the data structure or run the operations in parallel or concurrently and the code will still behave exactly the same. These nice properties mean that when I'm thinking about these sorts of tasks in any language, I think about what monoid or semigroup captures a given operation, rather than any of the mechanics of writing the loop. I find clarifies my thinking a lot.