在《第一原理的Haskell编程》一书中,有一个练习要求我在Monoid
类型上实例化Or
:
data Or a b =
Fst a
| Snd b
deriving (Eq, Show)
以前,我们在实例化Semigroup
时定义了规则:
instance Semigroup (Or a b) where
(Fst _) <> (Fst y) = Fst y
(Fst _) <> (Snd y) = Snd y
(Snd x) <> (Fst _) = Snd x
(Snd x) <> (Snd _)= Snd x
根据上述规则,显然Fst x
应该是mempty
,如果它是Monoid,其中x
是a
类型的任何东西。但是我不知道如何写规则:
instance (Monoid a, Monoid b) => Monoid (Or a b) where
mempty = ??? -- Please help me with this, to make every (Fst x) be mempty.
mappend = (<>)
答案 0 :(得分:6)
简而言之:您对(<>)
的定义不能用作monoid的二进制运算符。除非可以保证a
仅存在一个可能的值(如果我们使用b
作为“中性元素”的构造函数,则为Snd
)。
根据上述规则,如果
Fst x
是mempty
,显然应该是Monoid
。
的确,如果 是monoid [wiki]。但是对于一个半身像,可以证明确实存在一个一个身份元素。证明如下:给定有两个中性元素 e 1 和 e 1 ,则表示 e 1 ⊕e 2 = e 1 ,但同时 e 1 ⊕e 2 = e 2 (因为a⊕e=e⊕a= a 与保持一致> e 一个标识元素),因此表示 e 1 = e 2 成立,因此两者相同
现在您对(<>)
的定义中没有这样的标识元素。的确,假设此元素为Fst e
,则应认为:
Fst e <> Fst a = Fst a
包含(定义的第一行),但也应包含:
Fst a <> Fst e = Fst a
,这将根据您的(<>)
函数仅在a
为e
时成立。因此,我们可以这样声明一个Monoid的唯一方法是,如果我们只能在Fst
构造函数中定义 one 值,例如@leftroundabout这样说:
instance Monoid (Or () b) where
mempty = Fst ()
mappend = (<>)
因此,我们可以得出结论,您的(<>)
函数不不适合作为Monoid的二进制运算符。您将需要提供一个不同的二进制运算符,该二进制运算符的结构方式使其可以在monoid中使用。
现在,identity元素仍然有可能是Snd e
的形式,但是再次出现:
(Snd x) <> (Snd e) = Snd x
(Snd e) <> (Snd x) = Snd x
应该都成立,而在您的实现中:
(Snd x) <> (Snd _)= Snd x
后者将不成立(因为x
可以与e
不同)。