[英]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.