I can more easily answer this question for comparing monoids to semirings (i.e. like rings, but without negation).
The easiest way to understand semirings is that they most commonly arise when a type has two valid Monoid
instances that interact with each other in a specific way. Rather than defining two separate newtypes (one for each Monoid
instance), it's much easier to use the semiring operations to distinguish which Monoid
instance we meant.
An example of this is the Bool
type in Haskell, which has two valid Monoid
instances, which we distinguish using the Any
and All
newtypes:
newtype Any = Any { getAny :: Bool }
newtype All = All { getAll :: Bool }
instance Monoid Any where
mempty = Any False
(Any b1) `mappend` (Any b2) = Any (b1 || b2)
instance Monoid Any where
mempty = And True
(And b1) `mappend` (And b2) = And (b1 && b2)
It's a pain in the butt to disambiguate these two instances using the Any
/All
newtypes, but if we use a semiring then we can avoid the newtypes entirely by using 0
/(+)
to correspond to one of the Monoid
instances (in this case Any
) and 1
/(*)
to correspond to the other Monoid
instance (in this case And
):
instance Num Bool where
fromIntegral 0 = False
fromIntegral 1 = True
(+) = (||)
(*) = (&&)
Another example of two competing Monoid
instances are Sum
s and Product
s for numbers:
newtype Sum a = Sum { getSum :: a }
newtype Product a = Product { getProduct :: a }
instance Num a => Monoid (Sum a) where
mempty = Sum 0
(Sum x) `mappend` (Sum y) = Sum (x + y)
instance Num a => Product (Product a) where
mempty = Product 1
(Product x) `mappend` (Product y) = Product (x * y)
Usually it's much easier to use (+)
/(*)
directly to disambiguate which of the two Monoid
s we meant.
Notice that in both cases (bools and numbers) the two Monoid
instances in question interact with each other in the following way:
x * (y + z) = (x * y) + (x * z)
x * 0 = 0
This is actually an example of a functor law in disguise. If you define:
fmap = (x *)
(.) = (+)
id = 0
Then it's the same thing as saying:
fmap (y . z) = fmap y . fmap z
fmap id = id
So you don't necessarily want to use a semiring for anything that implements two separate Monoid
instances. You also want to verify that those two Monoid
instances obey the distributive/zero laws (i.e. functor laws), too.