我想实现一个名为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
|
为什么我可以把这两个部分编译在一起,但是当我去掉半群的时候,会出现错误呢?这个半群是什么东西
2条答案
按热度按时间qyyhg6bp1#
Semigroup
是所有类型中的一个类,它实现了<>
操作,在某种程度上它是关联的(即a<>(b<>c) ≡ (a<>b)<>c
,如果我们忽略小的浮点偏差,它确实适用于复数)。Monoid
是另外具有中立元素mempty
的半群的类,即总是满足mempty <> a ≡ a <> mempty ≡ a
的元素(对于具有加法和零的复数也成立)。对于一个没有
<>
操作的类型来说,这是一个毫无意义的要求,也就是说,它没有一个Semigroup
示例。这可以用Semigroup
是Monoid
的一个 * 超类 * 来表示,因此,不可能有一个类型是Monoid
的一个示例,而不是Semigroup
的一个示例。Semigroup
和Monoid
是独立的类,旧的Monoid
提供了自己的mappend
操作,相当于现代的<>
。一些旧的书籍/教程仍然基于这种旧的类层次结构。但是因为有一堆类型是 * 唯一 * 半群,而不是幺半群,所以类的层次结构被改变了。
qf9go6mv2#
一个monoid [wiki]是一个具有恒等式的semigroup [wiki]:它有一个identity element e [wiki],对于它的结合二元运算**,对于集合中的所有元素 x,将 x e = e x = x。因此,每个 * 幺半群 * 都是一个 * 半群 *,因为 * 半群 * 是一个具有结合二元运算的代数结构,除了不需要单位元 e 之外,它的性质与幺半群相同。
从
base-4.9.0.0
开始,引入了一个Semigroup
类,但是Semigroup
和Monoid
**没有“共存”。因为为Semigroup
和Monoid
示例定义不同的二元运算符没有多大意义(因为这很可能会引起混淆),因为base-4.11.0.0
,Semigroup
是Monoid
的一个 * 超类 *。实际上,Monoid
被定义为[src]:这意味着要使某个对象成为
Monoid
的示例,它必须同时也是Semigroup
的示例。**mappend :: Monoid a => a -> a -> a
将自动链接到(<>) :: Semigroup a => a -> a -> a
**的实现,并且最终可能被删除。因此,如果不定义
Semigroup
的示例,就不能再定义Monoid
的示例。但是,由于您只是将mappend
的实现从Monoid
示例移动到Semigroup
的示例,因此可以“机械地”从Monoid
的旧示例创建示例。