I wanted to implement a Custom Datatype named ComplexNumber like this:
data ComplexNumber a = C (a,a)
Now I wanted to implement the Monoid Variable and define the binary mempty 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 dont really understand) and came to a soloution that atleast 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 programm doesn't compile anymore and gives me following 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
CodePudding user response:
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.
CodePudding user response:
A monoid [wiki] is a semigroup [wiki] with identity: it has an identity element e [wiki] for which the associative binary operation ⊕ will x ⊕ e = e ⊕ x = x for all elements x in the set. Every monoid is thus a semigroup, since a semigroup is an algebraic structure with an associative binary operator ⊕ for which the same properties thus hold as in a monoid, except that there is no need for an identity element e.
Since base-4.9.0.0
, a Semigroup
class has been introduced, but then Semigroup
and Monoid
did not "live together". Since it does not make much sense to define a different binary operator for the Semigroup
than for the Monoid
instance (since that would likely introduce confusion), since base-4.11.0.0
, Semigroup
is a superclass of Monoid
. Indeed, Monoid
is defined as [src]:
class Semigroup a => Monoid a where # …
This means that in order to make something an instance of Monoid
, it has to be an instance of Semigroup
as well. The mappend :: Monoid a => a -> a -> a
will automatically link to the implementation of (<>) :: Semigroup a => a -> a -> a
and will, likely, eventually be removed.
You thus can no longer define an instance of Monoid
without defining an instance of Semigroup
. But you can "mechanically" create an instance from an old instance of Monoid
, since you simply move the implementation of mappend
from the Monoid
instance to the instance for Semigroup
.