这种类型的身份是什么?

时间:2017-06-26 14:25:39

标签: haskell math monoids

我有以下数据类型:

data Bull = Fools
  | Twoo
  deriving (Eq, Show)

并使用Monoid实现它:

instance Monoid Bull where
  mempty = Fools
  mappend _ _ = Fools

如您所见,mempty是身份法不具备的身份功能:

*Main> x = Twoo
*Main> mappend mempty x == x

Bull类型的身份是什么? Bool类型的身份是什么?

3 个答案:

答案 0 :(得分:14)

简短回答取决于mappend功能

  

Bull类型的身份是什么? Bool类型的身份是什么?

类型具有无“固有”标识标识元素仅存在关于二元函数(此处为{{1} }),就像Wikipedia article says

一样
  

在数学中,一个单位元素或中性元素是一组特殊类型的元素,相对于该集合上的二元运算,当与它们结合时,其他元素保持不变。

所以这取决于操作mappend是什么。

如果我们定义mappend Bool,则标识元素为mappend = (&&)。但是,如果我们选择mempty = True,那么mappend = (||)

您的mempty = False 不正确。由于它不能满足财产:

instance Moniod Bull

如果我们选择mappend mempty x = x 作为Fools,则mempty = Fools应为mappend Fools Twoo。如果我们选择Twoo,则mempty = Twoo仍然不是mappend Twoo Twoo

Twoo的要点是你必须仔细设计二元运算符。就像Haskell documentation on Monoid所说的那样,它应该满足以下要求:

Monoid

这些规则并非为Haskell“发明”: monoid 是众所周知的algebraic structure。通常在数学中,幺半群表示为3元组。例如(N,+,0) N 此处设置(例如自然数), + 二进制函数,< em> 0 标识元素。

答案 1 :(得分:6)

这是一个很好的问题,也是我以前玩过几次的问题。事实上,这是我提出的universe的第一个用途之一,我仍然认为它是一个整洁的用途。那么,让我告诉你!

以下是我们的想法:我们将使用Universe包来枚举 memptymappend的所有可能实现,然后检查哪些符合法律。首先,一些样板:

import Data.Universe
import Data.Universe.Instances.Reverse

data Bull = Fools | Twoo deriving (Bounded, Enum, Eq, Ord, Read, Show)
instance Universe Bull
instance Finite Bull

这只是导入包的相应位并定义您的类型。现在,让我们编写幺半群法则。我们希望我们的mappend是关联的;为(+)撰写mappend,我们可以要求:

associative        (+) = all (\(x,y,z) -> (x+y)+z == x+(y+z)) universe

身份法律彼此非常相似,并将我们的mappend与我们的mempty联系起来(我们会在此处拨打(+)zero):

leftIdentity  zero (+) = all (\x -> zero+x == x) universe
rightIdentity zero (+) = all (\x -> x+zero == x) universe

幺半群应该满足所有三个法则:

monoid (zero, (+)) = associative (+) && leftIdentity zero (+) && rightIdentity zero (+)

现在我们可以通过过滤出符合法律的那些来构建所有幺半群的列表:

monoidsOnBull :: [(Bull, Bull -> Bull -> Bull)]
monoidsOnBull = filter monoid universe

让我们在ghci中查看:

> mapM_ print monoidsOnBull
(Twoo,[(Fools,[(Fools,Fools),(Twoo,Fools)]),(Twoo,[(Fools,Fools),(Twoo,Twoo)])])
(Fools,[(Fools,[(Fools,Fools),(Twoo,Twoo)]),(Twoo,[(Fools,Twoo),(Twoo,Fools)])])
(Twoo,[(Fools,[(Fools,Twoo),(Twoo,Fools)]),(Twoo,[(Fools,Fools),(Twoo,Twoo)])])
(Fools,[(Fools,[(Fools,Fools),(Twoo,Twoo)]),(Twoo,[(Fools,Twoo),(Twoo,Twoo)])])

(旁白:我们应该如何阅读此输出?嗯,Universe包显示类型a -> b的函数,方法是显示类型[(a, b)]的图形,即输入和输出对的列表。上面输出的每一行都是一个元组,在第一部分中有一个合适的mempty,在第二部分中有一个合适的mappend。)

那么这些幺半群做什么?让我们一次拿一个:

(Twoo,[(Fools,[(Fools,Fools),(Twoo,Fools)]),(Twoo,[(Fools,Fools),(Twoo,Twoo)])])

除非两个输入均为mappend,否则Fools输出Twoo。也就是说,这是Bull相当于(&&)。在(&&)的案例中,True的身份为Twoo - 或Bull

(Fools,[(Fools,[(Fools,Fools),(Twoo,Twoo)]),(Twoo,[(Fools,Twoo),(Twoo,Fools)])])

如果两个输入相等,则此mappend输出Fools,否则输出Twoo。您可以将此视为Bool上的xor,或者1位数字上的两个补码。它的身份是Fools(或零)。

(Twoo,[(Fools,[(Fools,Twoo),(Twoo,Fools)]),(Twoo,[(Fools,Fools),(Twoo,Twoo)])])

这个就像最后一个,但在任何地方都被否定了。

(Fools,[(Fools,[(Fools,Fools),(Twoo,Twoo)]),(Twoo,[(Fools,Twoo),(Twoo,Twoo)])])

这个就像第一个,但到处都是否定的。它也恰好与(||) Bool上的False一样,其身份为base

这结束了讲座,但还有另外两个有趣的补充说明。

首先,当mappend分别为(&&)(||)时,Monoid会提供AllAny幺半群。据我所知,没有一个合适的新类型可以得到xor或它的否定为Num;但是你可以通过为Bool声明一个Word1实例来伪造它(使用False直觉True为0而Sum Bool为1)来获取它data Color = Red | Green | Blue

第二,这里的另一个答案是:> length monoidsOnColor 33 有什么幺半群?我们现在有了解决这个问题的所有机制,并确认实际上有很多幺半群:

{{1}}

我鼓励您尝试构建将所有内容列出的代码,并通过它们查看您可以获得的见解!

答案 2 :(得分:3)

对于给定集合(或类型,在Haskell中)没有单个monoid。实际上,monoid中的标识不是由定义它的集合决定的,而是由操作(在Haskell中称为mappend)决定的。例如,可以在添加(带有标识0)或产品(带有标识1)时定义整数上的幺半群。

这就是SumProduct类型存在的原因:因为在Monoid集上有Num a => a类型类的多种可能实现,我们更喜欢换行将其转换为newtype并在包装类型上定义Monoid实现。

Bool类型有All类似的结构,在{(1 {1}})的联合((&&))下的布尔值为monoid,标识为TrueAny,在具有身份(||)的分离(False)下的布尔值上的幺半群。事实上,布尔可以在许多其他操作(例如XOR和XNOR门)上形成幺半群。

由于Bull类型与Bool类型同构(两者都有两个无效的构造函数),因此您可以通过MonoidBool的实现来激发自己的灵感,但是我们无法通过进一步的背景来确定哪种实现最适合您的情况。

另外,正如Anton Xue所提到的,即使你Bull定义一个幺半群,它真的有意义吗?你的类型应该代表什么?