在 Haskell 中 Maybe a
也是能具有 Monoid
特性的。
instance Monoid a => Monoid (Maybe a) where
mempty = Nothing
Nothing `mappend` m = m
m `mappend` Nothing = m
Just m1 `mappend` Just m2 = Just (m1 `mappend` m2)
首先我們先注意一下我們這裡是用 instance Monoid a => Monoid (Maybe a)
而不是 Monoid Maybe
,因為在定義 Monoid 的 instance 時是需要一個明確的 type ,所以這裡就會是使用 Maybe a
。 也就代表當 a
為 Monoid
時 Maybe a
才會是 Monoid
。
其實 list 在實作
Monoid
時也是如此,只是 list 所裝的元素 type 跟他是否有辦法實作出Monoid
沒有關係,但Monoid
依然會需要是一個明確的 type 所以才會是這個instance Monoid [a]
不用型別約束的樣子 。
剩下就是 mempty
是用 Nothing
,以及如果兩個 Just
進行 mappend
運算時是將 m1
及 m2
進行 mappend
運算在用 Just
wrap 起來
Nothing `mappend` Just "1" -- Just "1"
Just "1" `mappend` Nothing -- Just "1"
Just (Sum 1) `mappend` Just (Sum 2) -- Just (Sum {getSum = 3})
Just [1] `mappend` Just [2,3] -- Just [1,2,3]
那這麼做的好處是什麼?我想比較大的用處是在我們處理複數個 Maybe
時,我們可以簡單地合併這些 Maybe
而不用擔心它是 Nothing
還是 Just a
。
其實 semigroup 是比 monoid 更加基礎的概念,他就只是定義了遵守結合律的二元函數運算。
其實所謂的二元函數運算就是 Magma 這個數學概念,它定義一個集合及運算
(S,*)
,而S
為一非空集合,*
是一個二元運算它將集合中兩個元素進行運算後會映射到集合中其中一個元素。 像是加法就是一種 Magma ,它可以接受兩個實數相加後會映射會原本的實數集合裡。
class Semigroup a where
(<>) :: a -> a -> a
這邊可以看到我們只定義了一個 function <>
他就只是一個接受兩個 type 為 a
的值並回傳一個type 為 a
的值,而當然他要遵守結合律才能稱為 semigroup 。
所以我們可以得出幾個小結論
Monoid
都是 Semigroup
Semigroup
就是定義了元素間組合的行為。Monoid
多了 identity 這個規則也因此 Monoid
對於空值的處理會更加簡單。寫到這裡開始覺得當在 Haskell 遇到愈抽象的概念表示它愈接近數學上的定義了,總而言之不管 Monoid
、Semigroup
及 Functor
都是藉由 typeclass 來定義我們該符合哪些行為及特性來符合數學上的某些性質,而這些性質都是為了能我們的程式碼更加的抽象(或者該說泛用)。