[英]What is the main difference between Free Monoid and Monoid?
看起来我对 Haskell 中的Monoid
有很清楚的了解,但上次我听说了一种叫做自由幺半群的东西。
什么是自由幺半群?它与幺半群有什么关系?
你能在 Haskell 中提供一个例子吗?
在编程环境中,我通常将free monoid翻译为[a]
。 在 Bartosz Milewski 为程序员撰写的关于范畴论的优秀系列文章中,Bartosz Milewski 将 Haskell 中的自由幺半群描述为列表幺半群(假设忽略了无限列表的一些问题)。
标识元素为空列表,二元运算为列表拼接:
Prelude Data.Monoid> mempty :: [Int]
[]
Prelude Data.Monoid> [1..3] <> [7..10]
[1,2,3,7,8,9,10]
直觉上,我认为这个幺半群是“自由的”,因为它是一个你总是可以应用的幺半群,不管你想使用什么类型的值(就像自由 monad 是一个你总是可以从任何函子创建的 monad) .
此外,当一个类型存在多个幺半群时,自由幺半群会推迟决定使用哪个特定的幺半群。 例如,对于整数,存在无限多个幺半群,但最常见的是加法和乘法。
如果您有两个(或更多整数),并且您知道您可能想要聚合它们,但您还没有决定要应用哪种类型的聚合,您可以改为使用免费的幺半群来“聚合”它们 - 实际上,这意味着将它们放在一个列表中:
Prelude Data.Monoid> [3,7]
[3,7]
如果您稍后决定要将它们添加在一起,那么这是可能的:
Prelude Data.Monoid> getSum $ mconcat $ Sum <$> [3,7]
10
相反,如果您希望将它们相乘,您也可以这样做:
Prelude Data.Monoid> getProduct $ mconcat $ Product <$> [3,7]
21
在这两个示例中,我特意选择将每个数字提升为包含更具体幺半群的类型( Sum
、 Product
),然后使用mconcat
执行聚合。
对于加法和乘法,有更简洁的方法可以做到这一点,但我这样做是为了说明如何使用更具体的幺半群来解释自由幺半群。
正如你已经知道的,幺半群是一个包含元素e
和操作<>
满足的集合
e <> x = x <> e = x (identity)
(x<>y)<>z = x<>(y<>z) (associativity)
现在,一个自由幺半群,直观地,是一个只满足上述方程的幺半群,显然,满足它们的所有结果。
例如,Haskell 列表幺半群([a], [], (++))
是免费的。
相比之下,Haskell sum monoid (Sum Int, Sum 0, \\(Sum x) (Sum y) -> Sum (x+y))
不是免费的,因为它也满足附加方程。 例如,它是可交换的
x<>y = y<>x
这不是从前两个方程得出的。
请注意,可以证明,在数学中,所有自由幺半群与列表幺半群[a]
同构。 因此,编程中的“自由幺半群”只是任何数据结构的花哨术语,它 1) 可以转换为列表,然后返回,不会丢失信息,2) 反之亦然,列表可以转换为列表,并返回,不会丢失信息。
在 Haskell 中,你可以用“类列表类型”替代“自由幺半群”。
自由幺半群是一种特定类型的幺半群。 具体来说,它是通过将一些固定的元素集作为字符然后从这些元素形成所有可能的字符串而获得的幺半群。 这些字符串的底层操作是字符串连接,形成一个幺半群,这个幺半群称为自由幺半群。
幺半群(M,•,1)
是一个数学结构,使得:
M
是一个集合1
是M
的成员• : M * M -> M
a•1 = a = 1•a
M
元素a
、 b
和c
,我们有a•(b•c) = (a•b)•c
。 集合M
上的自由幺半群是幺半群(M',•,0)
和函数e : M -> M'
使得对于任何幺半群(N,*,1)
,给定一个(集合)映射f : M -> N
我们可以将其扩展为幺半群态射f' : (M',•,0) -> (N,*,1)
,即
f a = f' (e a)
f' 0 = 1
f' (a•b) = (f' a) • (f' b)
换句话说,它是一个没有任何特殊作用的幺半群。
一个幺半群的例子是运算为加法且单位为 0 的整数。另一个幺半群是整数序列,运算为串联,单位为空序列。 现在加法下的整数不是整数上的自由幺半群。 考虑映射到整数序列的n
到(n)
。 然后为了让它免费,我们需要将它扩展到一个将n + m
带到(n,m)
的映射,即它必须从0
到(0)
和到(0,0)
到(0,0,0)
等。
另一方面,如果我们尝试将整数序列视为整数上的自由幺半群,我们会发现它似乎在这种情况下有效。 将映射扩展为带加法的整数是取序列之和(() 之和为 0)。
那么集合S
上的自由幺半群是什么? 我们可以尝试的一件事就是S
任意二叉树。 在 Haskell 类型中,这看起来像:
data T a = Unit | Single a | Conc (T a) (T a)
并且它的标识为Unit
, e = Single
和(•) = Conc
。
我们可以编写一个函数来显示它是如何免费的:
-- here the second argument represents a monoid structure on b
free :: (a -> b) -> (b -> b -> b, b) -> T a -> b
free f ((*),zero) = f' where
f' (Single a) = f a
f' Unit = zero
f' (Conc a b) = f' a * f' b
它应该是相当明显的,这满足需要在自由幺法律a
。 除了一个: T a
不是幺半群,因为它不完全满足定律 4 或 5。
所以现在我们应该问我们是否可以把它变成一个更简单的自由幺半群,即一个真正的幺半群。 答案是肯定的。 一种方法是观察Conc Unit a
和Conc a Unit
和Single a
应该是一样的。 因此,让我们使前两种类型不可表示:
data TInner a = Single a | Conc (TInner a) (TInner a)
data T a = Unit | Inner (TInner a)
我们可以进行的第二个观察是Conc (Conc ab) c
和Conc a (Conc bc)
之间应该没有区别。 这是由于上述第 5 条。 然后我们可以展平我们的树:
data TInner a = Single a | Conc (a,TInner a)
data T a = Unit | Inner (TInner a)
Conc
的奇怪构造迫使我们只有一种方法来表示Single a
和Unit
。 但是我们看到我们可以将所有这些合并在一起:将Conc
的定义更改为Conc [a]
然后我们可以将Single x
更改为Conc [x]
并将Unit
更改为Conc []
所以我们有:
data T a = Conc [a]
或者我们可以写:
type T a = [a]
操作是:
unit = []
e a = [a]
(•) = append
free f ((*),zero) = f' where
f' [] = zero
f' (x:xs) = f x * f' xs
所以在 Haskell 中,列表类型被称为自由幺半群。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.