2012-01-08 69 views
40

我學習Haskell的方法我開始掌握monad概念,並開始在代碼中使用已知的monad,但從設計師的角度來看,我仍然遇到難題。在面向對象中有幾個規則,比如「爲名稱標識名詞」,注意某種狀態和接口......但是我無法爲monad找到相應的資源。如何識別monadic設計模式?

那麼如何將自然界中的一個問題確定爲一元問題呢? monadic設計有什麼好的設計模式?當你意識到某些代碼可以更好地重構爲monad時,你的方法是什麼?

+8

這個問題「這個問題應該單獨解決」,還有更多「這個問題應該用[某些數據類型]來解決,嘿!多麼方便,[這個數據類型]是Monad的一個實例,給我噸合作的可組合性。「 – 2012-01-08 22:12:04

+0

@DanBurton:無論是面向對象,程序,功能,回溯邏輯,連接還是任何其他類型的語言,當然都可以採用任何其他類型的設計模式。 – 2012-01-10 12:55:41

回答

57

有用的經驗法則是當您在上下文中看到值;單子可以看作是分層的「效果」:

  • 可能:偏袒(用途:能不能計算)
  • 或者:短路錯誤(用途:錯誤/異常處理)
  • [](列表單子):非確定性(用途:列表生成,過濾,...)
  • 州:單個可變引用(使用:狀態)
  • 讀卡器:共享環境(使用:變量綁定,公共信息,...)
  • 編劇: a 「側通道」 輸出或積累(用途:日誌,維護一個只寫計數器, ...)
  • 續:非本地控制流(用途:不勝枚舉)

通常情況下,你通常應該從標準Monad Transformer Library分層的單子變壓器設計你的單子,它可以讓你將上面的效果組合成一個單一的模式河畔。它們一起處理您可能想要使用的大部分monad。 MTL中沒有包含一些額外的單子,如probabilitysupply單子。

至於開發一個新定義的類型是否是一個單子,以及它如何表現爲一個,您可以通過從FunctorMonad漲認爲它的一個直覺:

  • 函子可以讓您用純函數轉換值。
  • 應用允許您嵌入純數值並表達應用程序 - (<*>)可讓您從嵌入式函數及其嵌入式參數轉到嵌入式結果。
  • Monad允許嵌入式計算的結構取決於先前計算的

明白最簡單的方法是看的join類型:

join :: (Monad m) => m (m a) -> m a 

這意味着,如果你有一個嵌入式計算,其結果是嵌入式計算,你可以創建執行該計算結果的計算。因此,您可以使用monadic效果根據以前的計算值創建新計算,並將控制流轉移到該計算。

有趣的是,這可以是結構化的東西弱點 monadically:與Applicative,計算的結構是靜態的(即,一個給定的Applicative計算具有不能更改基於中間值的影響的一定的結構),而與Monad它是動態的。這可以限制你可以做的優化;例如,應用解析器不如一元解析器強大(嗯,這不是strictly true,但實際上它是),但它們可以更好地優化。

注意(>>=)可以定義爲

m >>= f = join (fmap f m) 

等單子可以簡單地用returnjoin(假設它被定義是一個Functor;所有的單子都是合用的函子,但Haskell的類型類層次結構遺憾的是不要求這是historical reasons)。作爲一個補充說明,無論他們從誤導的非Haskellers中得到什麼樣的嗡嗡聲,你可能都不應該過分關注單子。有很多代表有意義和強大模式的類型類,並不是所有的東西都最好用monad表示。 Applicative,Monoid,Foldable ......使用哪種抽象取決於你的情況。當然,僅僅因爲某件事是一個monad並不意味着它不能成爲其他事物;作爲一個monad只是另一種類型的屬性。

所以,你不應該過多地考慮「識別單子」。這些問題更像是:

  • 此代碼是否可以用更簡單的monadic形式表示?用哪個monad?
  • 是這種類型,我剛剛定義了monad?我可以利用單子上的標準函數編碼哪些通用模式?
+3

哦,非常好。如果我沒有睡覺的話,我會寫出很多答案,哈哈。男人,我幾乎不能跟上你們...... – 2012-01-08 18:31:29

+3

@ C.A.McCann:當有代表獲得時,你怎麼能睡覺? :) – ehird 2012-01-08 18:51:33

+0

@ehird +1你最近一直在開發大量的優秀答案。 – 2012-01-08 22:13:58

15

按照類型。

如果你發現你寫的功能與所有這些類型的

  • (a -> b) -> YourType a -> YourType b
  • a -> YourType a
  • YourType (YourType a) -> YourType a

或所有這些類型的

  • a -> YourType a
  • YourType a -> (a -> YourType b) -> YourType b

然後YourType可以是單子。 (我說「可能」,因爲功能必須服從法律的單子也。)

(請記住,您可以重新排序參數,因此如YourType a -> (a -> b) -> YourType b只是變相(a -> b) -> YourType a -> YourType b

不要看出來只爲單子!如果你把所有這些類型的

  • YourType
  • YourType -> YourType -> YourType

的功能,他們遵守法律幺,你有一個幺!這也可能是有價值的。對於其他類型類也是如此,最重要的是Functor。

6

有單子的效果圖:

  • 也許 - 偏袒/故障短路
  • 要麼 - 錯誤報告/短路(如可能包含更多信息)
  • 作家 - 只寫「狀態」,經常登錄
  • 閱讀器 - 只讀狀態,一般環境路過
  • 國家 - 讀/寫狀態
  • 恢復 - 可暫停計算
  • 名單 - 多成功

一旦你熟悉了這些影響其容易建立單子他們單子變壓器相結合。請注意,組合一些單子需要特別的照顧(特別是Cont和具有回溯的單子)。

有一件重要的事情要注意的是沒有太多單子。有一些外來的不在標準庫中,例如概率monad和Cont monad的變體如Codensity。但是除非你正在做一些數學上的事情,否則你會發明(或發現)一個新的monad,但是如果你使用Haskell足夠長的時間,你將會創建許多不同的標準monad組合。

編輯 - 另請注意,爲了您堆疊在不同的單子單子變壓器結果:

如果添加ErrorT(變壓器)的作家單子,你得到這個單子Either err (log,a) - 你只能訪問日誌,如果你沒有錯誤。

如果您將WriterT(transfomer)添加到Error monad,您會得到此monad (log, Either err a),它總是可以訪問日誌。

4

這是一種無法回答的問題,但我覺得反正說很重要。 只問! StackOverflow,/ r/haskell和#haskell irc頻道都是獲得智能人員快速反饋的好地方。如果你正在研究一個問題,並且你懷疑有一些monadic的魔法可以使它更容易,那就問問! Haskell社區喜歡解決問題,並且是非常友好的。

不要誤會,我不鼓勵你永遠不要爲自己學習。恰恰相反,與Haskell社區互動是其中一種學習方式。 LYAHRWH,2個免費在線提供的Haskell書籍,也強烈推薦。

呵呵,別忘了玩,玩,玩!當你使用monadic代碼玩耍時,你會開始感覺到「形狀」monad有什麼樣的感覺,以及monadic combinators是否有用。如果你正在推出自己的monad,那麼通常類型系統會引導你一個明顯的,簡單的解決方案。但說實話,你應該很少需要推出你自己的Monad實例,因爲Haskell庫提供了其他答案者提到的大量有用的東西。