2014-10-07 92 views
18

我在閱讀學習你一個哈斯克爾,我已經介紹了應用程序,現在我在monoids。我對這兩種理解都沒有問題,儘管我發現應用在實踐中很有用,monoid也不是那麼完美。所以我想我對Haskell不瞭解。什麼是monoids的實際使用?

首先,談到Applicative,它創建了一些類似於統一語法的操作來對「容器」執行各種操作。所以我們可以用正常功能對Maybe,列表執行操作,IO(?我應該說的單子我不知道單子還),功能:

λ> :m + Control.Applicative 
λ> (+) <$> (Just 10) <*> (Just 13) 
Just 23 
λ> (+) <$> [1..5] <*> [1..5] 
[2,3,4,5,6,3,4,5,6,7,4,5,6,7,8,5,6,7,8,9,6,7,8,9,10] 
λ> (++) <$> getLine <*> getLine 
one line 
and another one 
"one line and another one" 
λ> (+) <$> (* 7) <*> (+ 7) $ 10 
87 

所以應用性是一種抽象。我認爲我們可以沒有它,但它有助於清晰地表達一些想法模式,這很好。

現在,讓我們來看看Monoid。它也是抽象的,非常簡單。但它對我們有幫助嗎?從書中的每個例子似乎是顯而易見的,有更清晰的方式來做事:

λ> :m + Data.Monoid 
λ> mempty :: [a] 
[] 
λ> [1..3] `mappend` [4..6] 
[1,2,3,4,5,6] 
λ> [1..3] ++ [4..6] 
[1,2,3,4,5,6] 
λ> mconcat [[1,2],[3,6],[9]] 
[1,2,3,6,9] 
λ> concat [[1,2],[3,6],[9]] 
[1,2,3,6,9] 
λ> getProduct $ Product 3 `mappend` Product 9 
27 
λ> 3 * 9 
27 
λ> getProduct $ Product 3 `mappend` Product 4 `mappend` Product 2 
24 
λ> product [3,4,2] 
24 
λ> getSum . mconcat . map Sum $ [1,2,3] 
6 
λ> sum [1..3] 
6 
λ> getAny . mconcat . map Any $ [False, False, False, True] 
True 
λ> or [False, False, False, True] 
True 
λ> getAll . mconcat . map All $ [True, True, True] 
True 
λ> and [True, True, True] 
True 

因此,我們已經注意到了一些模式,並創造了新的類型的類......好吧,我喜歡數學。但從實際角度來看,Monoid有什麼意義?它如何幫助我們更好地表達想法?

+2

如果你想深入挖掘一些真正展現monid的力量的高級材料,請查看[finger trees](http://www.cs.ox.ac.uk/ralf.hinze/publications/FingerTrees.pdf )(「Data.Sequence」後面的引擎)。我敢肯定,現在我想不出有些簡單的例子。 – luqui 2014-10-07 08:01:16

+4

我建議你通過這篇出色的[Dan piponi's](http://blog.sigfpe.com/2009/01/haskell-monoids-and-their-uses.html)文章。 – Sibi 2014-10-07 08:04:18

+0

相關,但不重複的問題:[Data.Monoid中所有這些新類型包裝器的實際價值是什麼](http://stackoverflow.com/questions/22080564/whats-the-practical-value-of-all-those- newtype-wrappers-in-data-monoid) – AndrewC 2014-10-07 15:54:36

回答

25

加布裏埃爾岡薩雷斯在他的博客寫了很多關於你爲什麼要關心的信息,你真的應該關心。您可以閱讀它here(並參見this)。

這是關於可擴展性,架構& API的設計。這個想法是,還有的「傳統建築」,上面寫着:

結合了幾個組件一起A型的,以產生一個 「網絡」或B型「拓撲」

與問題這種設計是,隨着程序的擴展,重構時你的地獄也會隨之變化。

所以你想改變模塊A來改善你的設計或域名,所以你這樣做。哦,但現在依賴於A的模塊B & C中斷了。你修好B,太好了。現在你修復C.現在B再次爆發,因爲B還使用了C的一些功能。我可以永遠繼續下去,如果你曾經使用過OOP,那麼你也可以。

再有就是加布裏埃爾所說的「哈斯克爾架構」:從它取代部分

結合A型在一起的幾個部件產生相同的A型的新 組件,性格區分

這也解決了這個問題,優雅。基本上:不要將模塊分層或擴展成專門的模塊。
相反,組合。

所以現在鼓勵的是,不要說「我有多個X,所以讓我們用一個類型來表示他們的聯合」,你說「我有多個X,所以我們把它們組合成一個X」。或者用簡單的英語說:「讓我們首先製作可組合的類型。」 (你是否感覺到monoids的潛伏?)。

想象一下,您想爲您的網頁或應用程序製作一個表格,並且您擁有您創建的模塊「個人信息表格」,因爲您需要個人信息。後來你發現你也需要「更改圖片窗體」,所以很快就寫了。現在你說我想合併它們,所以讓我們製作一個「個人信息&圖片窗體」模塊。而在現實生活中可擴展的應用程序,這可以並且確實失去控制。也許不是形式,但證明,你需要撰寫和創作,所以你將最終獲得「個人信息&更改圖片&更改密碼&更改狀態&管理好友&管理收藏&更改視圖設置&請不要延長我了&請&請停止!& STOP !!!!「模塊。這不太好,你將不得不在API中管理這種複雜性。哦,如果你想改變任何東西 - 它可能有依賴關係。所以..是啊..歡迎來到地獄。

現在讓我們來看看其他的選擇,但首先讓我們來看看的好處,因爲它會引導我們到它:

這些抽象規模無限因爲他們總是保持 組合性,因此,我們永遠需要將頂層的進一步抽象 分層。這就是爲什麼你應該學習Haskell的一個原因:你學會如何構建平板體系結構。

聽起來不錯,所以,不要製作「個人信息表單」/「更改圖片表單」模塊,請停下來思考一下,如果我們可以在這裏製作任何可組合的東西。那麼,我們可以製作一個「表格」,對吧?也會更抽象。
然後,爲所需要的任何東西構建一個可以合理使用的方法,將它們組合在一起並像其他任何一樣獲取一個表單。

所以,你不會因爲使用兩種形式並獲得一種形式的關鍵而再次變得一團亂七八糟的複雜樹。所以Form -> Form -> Form。正如您已經清楚看到的那樣,此簽名是mappend的一個實例。

替代,和傳統的架構可能會像a -> b -> c然後c -> d -> e然後......

現在,形式也不算太富有挑戰性的;我們面臨的挑戰是如何在真實世界的應用程序中使用它要做到這一點,只需儘可能地問自己(因爲它可以帶來回報,正如你所看到的):我怎樣才能使這個概念成爲可組合的?並且由於monoids是一種簡單的方法來實現這一點(我們想要簡單)首先問自己:這個概念是一個幺半羣?

旁註:謝天謝地,Haskell會非常不鼓勵你擴展類型,因爲它是一種函數式語言(沒有繼承)。但是仍然有可能爲某種東西創建一個類型,爲某種東西創建另一個類型,並且在第三種類型中將兩種類型都作爲字段。如果這是爲了構圖 - 看看你是否可以避免它。

+2

+1大文章和答案! – Mark 2014-10-07 15:41:47

+1

您可能還想提及[這篇文章](http://www.haskellforall.com/2014/07/equational-reasoning-at-scale.html),它特別使用'Monoid'作爲運行示例。 – 2014-10-07 17:40:51

6

問題的關鍵在於,當您將Int標記爲Product時,您表達了要將整數相乘的意圖。並將它們標記爲Sum,並加在一起。

然後你可以在兩者上使用相同的mconcat。這用於例如在Foldable中,其中一個foldMap表示摺疊包含結構的想法,同時以特定monoid的方式組合元素。

9

好吧,我喜歡數學。但是從實際角度來看,Monoid有什麼意義呢?它如何幫助我們更好地表達想法?

這是一個API。一個簡單的。對於支持類型:具有零元件

  • 具有追加操作
  • 很多類型

    • 支持這些操作。因此,爲操作和API命名有助於我們更清楚地瞭解事實。

      API很好,因爲它們讓我們重用代碼並重用概念。意味着更好,更易維護的代碼。

    5

    一個非常簡單的例子是foldMap。只是通過插入不同類羣這個單一的功能,你可以計算:

    • firstlast元素,
    • sum或元素的product(從這個也是他們的平均等)
    • 檢查是否all元件或any具有給定的屬性,
    • 計算的最大或最小的元素,
    • 映射元素的集合(如列表,sets,字符串,Text,ByteString或ByteString Builder)並將它們連接在一起 - 它們都是monoid。

    此外,類羣是可組合的:如果ab是類羣,所以是(a, b)。因此,您可以輕鬆地在一次傳遞中計算幾個不同的monoidal值(如計算元素平均值時的總和和產品等)。

    雖然你可以做這一切,沒有類羣,使用foldrfoldl,它更麻煩,也往往事倍功半:例如,如果你有一個平衡二叉樹,你想找到它的最小和最大的元素,您不能同時使用foldr(或兩者都使用foldl),對於其中一種情況,總是會有O(n),而當使用foldMap與適當的monoids時,它將是O(log n)在兩種情況下都是

    而這只是一個單一的功能foldMap!還有許多其他有趣的應用程序。給一個,exponentiation by squaring是一個有效的計算能力的方式。但它並沒有與計算能力綁定在一起。你可以實現它的任何monoid,如果它的<>O(1),你有一個有效的計算方法n times x <> ... <> x。突然之間,您可以執行高效的矩陣求冪運算,並且僅用O(log n)乘法計算_。參見times1p半羣。請參閱Monoids and Finger Trees

    相關問題