【问题标题】:What are the implications of the differences between a monoid and a ring?幺半群和环之间的差异意味着什么?
【发布时间】:2014-03-01 04:59:47
【问题描述】:

这是 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

这是 Clojure 中的幺半群示例:

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

(plus-monoid) 

(plus-monoid 3 4)

(reduce plus-monoid [2 3 4]) 

这是 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]]

这是 Clojure based on this 中的环示例:

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

似乎 - (借用 Java 用语)difference between a ring and a monoid is that the ring 有一个“要实现的接口上的附加方法”。 (也许我错了)。现在对我来说,这会对关联性产生影响——但我还没有完全理解这一点。

我的问题是:幺半群和环之间的差异意味着什么?

【问题讨论】:

  • 仅供参考,因为 Clojure 中的 (+) 返回 0+ 已经满足了您对 plus-monoid 的需求(区别在于 + 也需要 1、3 或更多参数)。

标签: haskell clojure monoids


【解决方案1】:

其他方法是制作戒指所必需的,但还不够。环形结构是由管理方法行为及其交互的规则产生的。

例如,您可以实例化 Monad 并实施绑定和返回,而公然无视 Monad 法则,只要您选择正确的类型,Haskell 的类型检查器就会很高兴。称它为 Monad 并不会使它表现得像 Monad 应该的那样。

戒指也是如此。

特别是,如果您调用戒指的合同方法+ plus- neg* mul0 zero1 one 那么

  • +, 0*, 1 应该遵守幺半群定律。
  • - 应该在+ 下提供逆,即-a + a = 0
  • + 应该通勤,即a + b = b + a
  • * 应该分布在 + 上,即
    a * (b + c) = (a * b) + (a * c) (b + c) * a = (b * a) + (c * a)

如果您还需要 * 上下班并有一个逆,那么您将有一个字段。

【讨论】:

  • 这是对“monad 只是一个接口”类比的有用批评。
【解决方案2】:

在比较幺半群和半环(即像环,但没有否定)时,我可以更轻松地回答这个问题。

理解半环的最简单方法是,当一个类型有两个有效的Monoid 实例以特定方式相互交互时,它们最常出现。与其定义两个单独的新类型(每个Monoid 实例一个),使用半环操作来区分我们指的是哪个Monoid 实例要容易得多。

这方面的一个例子是 Haskell 中的 Bool 类型,它有两个有效的 Monoid 实例,我们使用 AnyAll 新类型来区分:

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)

使用Any/All newtypes 来消除这两个实例的歧义是一件很痛苦的事,但是如果我们使用半环,那么我们可以通过使用0/(+) 来完全避免新types Monoid 实例之一(在本例中为 Any)和 1/(*) 对应于另一个 Monoid 实例(在本例中为 And):

instance Num Bool where
    fromIntegral 0 = False
    fromIntegral 1 = True

    (+) = (||)
    (*) = (&&)

两个竞争的Monoid 实例的另一个示例是Sums 和Products 用于数字:

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)

通常直接使用(+)/(*) 来区分我们所指的两个Monoid中的哪一个要容易得多。

请注意,在这两种情况下(布尔值和数字),有问题的两个 Monoid 实例以下列方式相互交互:

x * (y + z) = (x * y) + (x * z)

x * 0 = 0

这实际上是变相的函子定律的一个例子。如果你定义:

fmap = (x *)

(.) = (+)

id = 0

那就和说的一样:

fmap (y . z) = fmap y . fmap z

fmap id = id

因此,您不一定要对实现两个独立 Monoid 实例的任何东西使用半环。您还想验证这两个 Monoid 实例是否也遵守分配/零定律(即函子定律)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-08-08
    • 2014-12-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-16
    • 2013-04-11
    • 2014-06-12
    相关资源
    最近更新 更多