Why prefer monoids over semigroups in Haskell? Why do we need a mem?

I can understand the reason for the mappend requirement and associativity for it. But why do we need an element of identity, what is its pragmatic usefulness? Or maybe I missed something important and without defining it in cases where all logic does not work. Thanks!

+6
source share
3 answers

Some applications require mempty . For example, what should be the result of foldMap ping over an empty list? There is no value of a that you could pass to the displayed function to get m . Thus, there must be a default. It is nice if it is an identity element of an associative operation - for example, it allows you to arbitrarily reorder / break the fold without changing the result. When folding over a tree, in fact a lot of mempty can be displayed in the middle, but you always know that they will eventually be “squeezed out”, so you will not depend on the exact tree layout for reproducible results.

However, you are right about your concern: Semigroup would be quite common, and it would probably be better if this class was used where mempty not needed (since there are actually some pretty different types of Semigroup , but not Monoid ) However, the early design of the standard library did not seem to consider this important enough to guarantee an additional class, so a lot of code began to rely on Monoid without the need for a monoid.

This means the same problem as with Monad : a lot of code really needed only Applicative or even just Functor , but for historical reasons Monad still lingered before AMP. Ultimately, the problem is that the Haskell class hierarchies cannot really be refined after the fact, only extended down and not up.

+12
source

These days, base classes are displayed for both Semigroup and Monoid (and plans take effect so that the latter implies the former), so you don’t have to choose between two abstractions - just use the weakest assumption suitable for your purpose.

However, Monoid seems more useful in everyday practice than Semigroup . Why is this? I can present a couple of arguments:

  • Things that are useful in programming, such as collections, tend to have semantics of zero or many; Monoid naturally abstracts compound collections of things in such a way that Semigroup does not. (For example, [] , the free Monoid , is a classic collection example.)
  • Semigroup types, but not Monoid tend to lose precision when you start composing them. Given two non-empty lists xs and ys , we know that xs <> ys has at least two elements. But his type only promises that he has at least one; useful information has been discarded.
  • From a social point of view, people are scared by the Cabal of Hell in order to avoid unnecessary addictions, to cope with "non-empty things." If you want to work with monads-sans- return and category-sans- id , installing the semigroupoids package requires a piece of the “Kmett platform”.
+4
source

The same as with all other abstractions: the more operations you have at your disposal, the more complex things you can abstract. As for Monoid in particular, here are a few of the most popular features for which Semigroup simply not enough:

 foldMap :: (Foldable t, Monoid m) => (a -> m) -> ta -> m fold :: (Foldable t, Monoid m) => tm -> m mconcat :: Monoid a => [a] -> a 
+3
source

Source: https://habr.com/ru/post/1012472/


All Articles