《Haskell趣學指南》筆記之 Monoid

Monoid 是一個類型類。 字典對 Monoid 的解釋是:app

獨異點,帶有中性元的半羣;函數

這應該是範疇論裏的東西,反正我目前是看不懂這個什麼羣。學習

咱們先學習 newtypethis

newtype 關鍵字

data 關鍵字能夠建立類型; type 關鍵字能夠給如今類型設置別名; instance 關鍵字可讓類型變成類型類的實例;spa

newtype 關鍵字是根據現有數據類型建立新類型。 newtype 跟 data 很像,可是速度更快,不過功能更少,只能接受值構造器,值構造器只能有一個參數。code

newtype CharList = CharList { getCharList :: [Char] } deriving (Eq, Show) 

ghci> CharList "this will be shown!" 
CharList {getCharList = "this will be shown!"} 
ghci> CharList "benny" == CharList "benny" 
True 
ghci> CharList "benny" == CharList "oisters" 
False 
複製代碼

Monoid

咱們先用 *++ 做比喻ci

  1. 它們都接受兩個參數
  2. 參數和返回值的類型都相同
  3. 存在一個這樣的值:做爲參數時,返回值與另外一個參數相同
    1. 1 * 某值 的結果都是某值
    2. [] ++ 某列表 的結果都是某列表
  4. 當有三個或更多參數時,不管在哪裏加括號改變執行順序,結果都同樣
    1. (3 * 4) * 53 * (4 * 5) 同樣
    2. ([1,2] ++ [3,4]) ++ [5,6][1,2] ++ ([3,4] ++ [5,6]) 同樣

再看 Monoid開發

一個 Monoid 的實例由一個知足結合律的二元函數和一個單位元組成。get

在 * 的定義中 1 是單位元,在++ 的定義中[] 是單位元。string

class Monoid m where
    mempty :: m
    mappend :: m -> m -> m
    mconcat :: [m] -> m
    mconcat = foldr mappend mempty 
複製代碼
  • mempty 是單位元
  • mappend 是二元函數,書中認爲這個函數命名爲 append 是不恰當的,由於它的做用並非追加
  • mconcat 接受一個 m 列表,而後經過 mappend 將其中的全部元素合成一個值
  • mconcat 默認用 foldr 實現

因此大部分實例只須要定義 mempty 和 mappend 就好了。默認 concat 大部分時候都夠用了。

monoid 定律

mempty `mappend` x = x 
x `mappend` mempty = x 
(x `mappend` y) `mappend` z = x `mappend` (y `mappend` z) 
-- 並不要求 a `mappend` b = b `mappend` a,這是交換律
複製代碼

Haskell 不會強制要求這些定律成立,因此開發者要本身保證。

Monoid 實例

  • 列表是 Monoid 實例
  • Int 是 Monoid 實例嗎?
    • Int 的加法知足 monoid 定律,單位元是 0
    • Int 的乘法也知足 monoid 定律,單位元是 1
    • 那麼 Int 應該以哪一種方式成爲 Monoid 實例?
    • 答案是均可以,這就要用到 newtype 關鍵字了

Product 和 Sum

Data.Monoid 導出了 Product,定義以下

newtype Product a=Product{getProduct::a}
    deriving(Eq,Ord,Read,Show,Bounded)
複製代碼

他的 Monoid 實例定義以下:

instance Num a=> Monoid ( Product a) where
    mempty=Product 1
    Product x `mappend` Product y= Product (x * y)
複製代碼

使用方法:

ghci> getProduct $ Product 3 `mappend` Product 9
27
ghci> getProduct $ Product 3 `mappend` mempty
3
ghci> getProduct $ Product 3 `mappend` Product 4 `mappend` Product 2
24
ghci> getProduct.mconcat.map Product $ [3,4,2]
24
複製代碼

Product 使得 Num 以乘法的形式知足 Monoid 的要求。

Sum 則是用加法:

ghci> getSum $ Sum 2 `mappend` Sum 9
11
ghci> getSum $ mempty `mappend` Sum 3
3
ghci> getSum.mconcat.mapSum $ [1,2,3]
6
複製代碼

書中還說了不少其餘相似的例子。

不過我更關注的是 monad,因此接直接跳過了。

相關文章
相關標籤/搜索