haskell 什么是半群,为什么它的行为如此怪异?

hgqdbh6s  于 2022-11-14  发布在  其他
关注(0)|答案(2)|浏览(170)

我想实现一个名为ComplexNumber的自定义数据类型,如下所示:data ComplexNumber a = C (a,a)
现在我想实现Monoid变量并定义二进制mempty元素和Map,如下所示:

instance Num a => Monoid (ComplexNumber a) where
    mempty = C (0,0)
    mappend = (C (a1, b1)) (C (a2, b2)) = C (a1 + a2, b1 + b2)

但这并不奏效,所以试图找出原因,并遇到了半群(我仍然不是很理解),并得出了一个解决方案,至少编译,似乎与以下内容:

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)

有趣的是,当我删除半群的实现时,程序不再编译,并给我以下错误:

* 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
  |

为什么我可以把这两个部分编译在一起,但是当我去掉半群的时候,会出现错误呢?这个半群是什么东西

qyyhg6bp

qyyhg6bp1#

Semigroup是所有类型中的一个类,它实现了<>操作,在某种程度上它是关联的(即a<>(b<>c) ≡ (a<>b)<>c,如果我们忽略小的浮点偏差,它确实适用于复数)。
Monoid是另外具有中立元素mempty的半群的类,即总是满足mempty <> a ≡ a <> mempty ≡ a的元素(对于具有加法和零的复数也成立)。
对于一个没有<>操作的类型来说,这是一个毫无意义的要求,也就是说,它没有一个Semigroup示例。这可以用SemigroupMonoid的一个 * 超类 * 来表示,因此,不可能有一个类型是Monoid的一个示例,而不是Semigroup的一个示例。

  • 以前,* SemigroupMonoid是独立的类,旧的Monoid提供了自己的mappend操作,相当于现代的<>。一些旧的书籍/教程仍然基于这种旧的类层次结构。

但是因为有一堆类型是 * 唯一 * 半群,而不是幺半群,所以类的层次结构被改变了。

qf9go6mv

qf9go6mv2#

一个monoid [wiki]是一个具有恒等式的semigroup [wiki]:它有一个identity element e [wiki],对于它的结合二元运算**,对于集合中的所有元素 x,将 x e = e x = x。因此,每个 * 幺半群 * 都是一个 * 半群 *,因为 * 半群 * 是一个具有结合二元运算的代数结构,除了不需要单位元 e 之外,它的性质与幺半群相同。
base-4.9.0.0开始,引入了一个
Semigroup类,但是SemigroupMonoid**没有“共存”。因为为SemigroupMonoid示例定义不同的二元运算符没有多大意义(因为这很可能会引起混淆),因为base-4.11.0.0SemigroupMonoid的一个 * 超类 *。实际上,Monoid被定义为[src]:

class **Semigroup a =>** Monoid a where
    # …

这意味着要使某个对象成为Monoid的示例,它必须同时也是Semigroup的示例。**mappend :: Monoid a => a -> a -> a将自动链接到(<>) :: Semigroup a => a -> a -> a**的实现,并且最终可能被删除。
因此,如果不定义Semigroup的示例,就不能再定义Monoid的示例。但是,由于您只是将mappend的实现从Monoid示例移动到Semigroup的示例,因此可以“机械地”从Monoid的旧示例创建示例。

相关问题