Вопрос

This is an example of a monoid in Haskell:

> import Data.Monoid
> Sum 5 <> Sum 6 <> Sum 10
Sum {getSum = 21}
> mconcat [Sum 5, Sum 6, Sum 10]
Sum {getSum = 21}
> getSum $ mconcat $ map Sum [5, 6, 10]
21
> getProduct $ mconcat $ map Product [5, 6, 10]
300

This is an example of a monoid in Clojure:

(defn plus-monoid
    ([]
        0)
    ([a b]
        (+ a b)))

(plus-monoid) 

(plus-monoid 3 4)

(reduce plus-monoid [2 3 4]) 

This is an example of a ring in Haskell:

module Rings where

newtype Matrix r = M [[r]] deriving (Eq,Show)

instance Num r => Num (Matrix r) where
   M [[a,b],[c,d]] + M [[a',b'],[c',d']] = M [[a+a',b+b'],[c+c',d+d']]
   negate (M [[a,b],[c,d]]) = M [[-a,-b],[-c,-d]]
   M [[a,b],[c,d]] * M [[e,f],[g,h]] = M [[a*e+b*g, a*f+b*h] ,[c*e+d*g, c*f+d*h]]
   fromInteger n = M [[fromInteger n, 0],[0, fromInteger n]]

> M [[1,2],[3,4]] - M [[1,0],[0,1]]
M [[0,2],[3,3]]
> M [[2,0],[0,3]] * M [[1,2],[3,4]]
M [[2,4],[9,12]]

This is an example of a ring in Clojure based on this:

(defprotocol ring
  (plus [x y])
  (mult [x y])
  (neg [x])
  (zero [])
  (one []) )

It seems to be - (to borrow Java parlance) that the difference between a ring and a monoid is that the ring has an "additional method on the interface to implement". (Perhaps I'm mistaken). Now to me this would have an impact on associativity - but I haven't got my head around the full implications of this.

My question is: What are the implications of the differences between a monoid and a ring?

Это было полезно?

Решение 2

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 Sums and Products 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 Monoids 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.

Другие советы

The additional methods are necessary but not sufficient to make a ring. The ring structure comes about by the rules governing the behavior of methods and their interaction.

For example, you could instance Monad and implement bind and return with flagrant disregard for the Monad laws and Haskell's type checker will be happy as long as you get the types right. Calling it a Monad does not make it behave like a Monad should though.

The same would be true for a ring.

In particular, if you call a ring's contractual methods + plus, - neg, * mul, 0 zero, 1 one then

  • +, 0 and *, 1 ought to obey monoid laws.
  • - ought to provide the inverse under +, i.e. -a + a = 0
  • + ought to commute, i.e. a + b = b + a
  • * ought to distribute over +, i.e.
    a * (b + c) = (a * b) + (a * c) and (b + c) * a = (b * a) + (c * a)

If you furthermore required * to commute and have an inverse, then you'd have a field.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top