haskellmonoidssemigroup

Haskell, implementing Monoids. What is Semigroup and why does it act so weird?


I wanted to implement a Custom Datatype named ComplexNumber like this:

 data ComplexNumber a = C (a, a) 

Now I want to implement the Monoid variable and define the binary empty element and the mappend like this:

instance Num a => Monoid (ComplexNumber a) where
    mempty = C (0,0)
    mappend = (C (a1, b1)) (C (a2, b2)) = C (a1 + a2, b1 + b2)

but this didn't work out, so tried to figure out why and came across Semigroup (which I still don't really understand) and came to a solution that at least compiles and seems to work with this:

instance Num a => Semigroup (ComplexNumber a) where
    (C (a1, b1)) <> (C (a2,b2)) = C (a1 + a2, b1 + b2)

instance Num a => Monoid (ComplexNumber a) where
    mempty = C (0,0)

The funny thing is, when I remove the implementation of Semigroup the program doesn't compile anymore and I get this error:

    * Could not deduce (Semigroup (ComplexNumber a))
        arising from the superclasses of an instance declaration
      from the context: Num a
        bound by the instance declaration at Aufgabe_10.hs:9:10-42
    * In the instance declaration for `Monoid (ComplexNumber a)'
  |
9 | instance Num a => Monoid (ComplexNumber a) where
  |  

Why is that I can compile those two sections together, but when I remove the semigroup an error occurs ? And what in particular is this Semigroup thing


Solution

  • Semigroup is just the class of all types that have an implementation of the <> operation, in a way so that it's associative (i.e. that a<>(b<>c) ≡ (a<>b)<>c, which does hold true for your complex numbers if we disregard small floating-point deviations).

    Monoid is the class of of semigroups which additionally have a neutral element mempty, i.e. an element that always fulfills mempty <> a ≡ a <> mempty ≡ a (also true for complex numbers with addition and zero).
    This would be a nonsensical requirement for a type that doesn't even have the <> operation, i.e. that doesn't have a Semigroup instance. This is expressed by Semigroup being a superclass of Monoid, and thus it's impossible to have a type which is an instance of Monoid but not of Semigroup.

    Historically, Semigroup and Monoid were separate classes, with the older Monoid shipping its own mappend operation that is the equivalent to the modern <>. Some older books / tutorials are still based on this old class hierarchy.
    But because there are a bunch of types that are only semigroups, but not monoids, the class hierarchy was changed.