这是一个很好的问题,我之前已经玩过好几次了。事实上,这是我想出的universe 的第一个用法,我仍然认为它是一个简洁的用法。所以让我告诉你!
这里的想法是:我们将使用 Universe 包来枚举 所有 mempty 和 mappend 的可能实现,然后检查哪些满足法律。首先,一些样板:
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 上的异或,或者 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 提供了 All 和 Any 半群,当您希望 mappend 分别为 (&&) 和 (||) 时。据我所知,没有合适的新类型来获取 xor 或其否定作为Monoid;但是您可以通过为Bool 声明一个Num 实例来伪造它(使用Word1 直觉,False 为0,True 为1)通过Sum Bool 获取它。
其次,这里的另一个答案是:data Color = Red | Green | Blue 有什么幺半群?我们现在有所有的机器来回答这个问题,并确认实际上存在相当多的幺半群:
> length monoidsOnColor
33
我鼓励您尝试构建将它们全部列出的代码,并仔细研究它们,看看您可以获得什么见解!