What's the difference between a magma and a semigroup?
In abstract algebra, a magma is an algebraic structure, and is the marriage of a set (or 'type') together with a binary operation. Similarly, a semigroup is an algebraic structure that too marries a set together with a binary operation. In both cases, the binary operation must be 'total' (Meaning it isn't a partial function over the set.). The difference between a magma and a semigroup is in the added stipulation for semigroups that the binary operation be associative. If a set is together with a binary operation, it is a magma, and if that operation is associative, then it is a semigroup.
In Haskell, the difference between semigroups and magmas isn't entirely clear, at least not by looking at their respective typeclass implementations:
-- The Semigroup typeclass (minimal) definition: class Semigroup a where (<>) :: a -> a -> a -- And a minimal typeclass definition for 'Magma': class Magma a where (<~>) :: a -> a -> a
If we were to define an instance of the 'Magma' typeclass for a list:
instance Magma [a] where (<~>) lst1 lsty2 = lst1 ++ lst2 -- or, more consicely instance Magma [a] where (<~>) = (++)
How does this differ from the instance of Semigroup for a list of variable type 'a'?
instance Semigroup [a] where (<>) = (++)
Other than the exact name of the function (Which was altered only to avoid an ambiguous occurrence error.), there's no difference. The fact that the binary operation in a Semigroup must be associative is completely absent from our definition of Semigroup. On the surface of it (And just by looking at the typeclass definitions.), the two typeclasses appear to be one and the same.
What would be really useful is if Haskell allowed us to define constraints within the typeclass, something like:
-- Note, this is NOT real Haskell syntax class Semigroup a where (<>) :: a -> a -> a given a1 <> (a2 <> a3) == (a1 <> a2) <> a3
Whilst something like this would be very nice to have, it's difficult to see how it would be implemented in practice.
It's possible to implement some level of property testing via QuickCheck, but this can't be done in a typeclass definition, and can only be done for each instance of a typeclass.
Haskell requires discipline, but learning to write code with the level of discipline that Haskell requires will lead inevitably lead to higher quality code and, more than likely, a higher quality coder too.