Haskell的大規模設計? [關閉]

設計/構建大型功能程序的好方法是什麼,特別是在Haskell中? html

我已經閱讀了不少教程(本身編寫一個方案是我最喜歡的,Real World Haskell緊隨其後) - 可是大多數程序都相對較小,並且是單一目的。 另外,我不認爲它們中的一些特別優雅(例如,WYAS中的大量查找表)。 git

我如今想要編寫更大的程序,包含更多移動部件 - 從各類不一樣來源獲取數據,清理數據,以各類方式處理數據,在用戶界面中顯示,持久化,經過網絡進行通訊等。一個最好的結構,這樣的代碼是易讀,可維護,並適應不斷變化的要求? github

有大量文獻針對大型面向對象的命令式程序解決了這些問題。 像MVC,設計模式等的想法是實現普遍目標的理想規定,例如在OO風格中分離關注點和可重用性。 此外,較新的命令式語言適合於「隨着您的成長而設計」的重構風格,在個人新手看來,Haskell彷佛不太適合。 數據庫

Haskell有相同的文獻嗎? 如何在功能性編程(單子,箭頭,應用等)中使用異域控制結構的動物園最好地用於此目的? 你能推薦什麼最佳實踐? 編程

謝謝! 設計模式

編輯(這是Don Stewart回答的後續行動): 安全

@dons提到:「Monads在類型中捕獲關鍵的建築設計。」 網絡

我想個人問題是:如何在純函數式語言中考慮關鍵的架構設計? 數據結構

考慮幾個數據流的示例和幾個處理步驟。 我能夠將數據流的模塊化解析器編寫爲一組數據結構,我能夠將每一個處理步驟實現爲純函數。 一個數據所需的處理步驟將取決於其值和其餘數據。 一些步驟以後應該是GUI更新或數據庫查詢等反作用。 架構

什麼是以正確的方式綁定數據和解析步驟的「正確」方法? 人們能夠編寫一個大功能,爲各類數據類型作正確的事情。 或者可使用monad來跟蹤到目前爲止已處理的內容,並讓每一個處理步驟從monad狀態得到接下來須要的任何內容。 或者能夠寫不少單獨的程序併發送消息(我不太喜歡這個選項)。

他連接的幻燈片有一個咱們須要的東西子彈:「將設計映射到類型/函數/類/ monad上的成語」。 什麼是成語? :)


#1樓

也許你必須退後一步,想想如何將問題的描述轉化爲設計。 因爲Haskell是如此高級,它能夠以數據結構的形式捕獲問題的描述,將過程的動做和做爲函數的純轉換捕獲。 而後你有一個設計。 編譯此代碼並在代碼中查找有關缺乏字段,缺乏實例和缺乏monadic轉換器的具體錯誤時,開始開始,由於例如,您在IO過程當中須要某個狀態monad的庫中執行數據庫Access。 瞧,有節目。 編譯器提供您的心理草圖,並使設計和開發保持一致。

經過這種方式,您從一開始就受益於Haskell的幫助,編碼很天然。 若是你想到的是一個具體的普通問題,我不會在作「功能性」或「純粹」或足夠的通常性事物。 我認爲過分工程是IT中最危險的事情。 當問題是建立一個抽象一組相關問題的庫時,狀況就不一樣了。


#2樓

Gabriel的博客文章可擴展程序架構可能值得一提。

Haskell設計模式與主流設計模式的區別在於一個重要方面:

  • 傳統架構 :將A類的幾個組件組合在一塊兒,生成B類「網絡」或「拓撲」

  • Haskell架構 :將A類的幾個組件組合在一塊兒,生成相同類型A的新組件,其特徵與其取代部分沒法區分

一般狀況下,一種看似優雅的建築每每會從圖書館中脫穎而出,這種圖書館以自下而上的方式展示出這種良好的同質感。 在Haskell中,這一點尤爲明顯 - 傳統上被認爲是「自上而下的架構」的模式每每會被捕獲在像mvcNetwireCloud Haskell這樣的庫中。 也就是說,我但願這個答案不會被解釋爲嘗試取代這個線程中的任何其餘人,只是結構選擇能夠而且應該理想地由域專家在庫中抽象出來。 在我看來,構建大型系統的真正困難在於評估這些圖書館的建築「善」與全部實際問題。

正如liminalisht在評論中提到的那樣, 類別設計模式是Gabriel關於該主題的另外一篇文章,相似地。


#3樓

在Haskell的工程大項目 以及XMonad設計和實現中談到了這一點。 大型工程是關於管理複雜性。 Haskell中用於管理複雜性的主要代碼結構機制是:

類型系統

  • 使用類型系統來強制執行抽象,簡化交互。
  • 經過類型強制實施關鍵不變量
    • (例如,某些值沒法逃脫某些範圍)
    • 某些代碼沒有IO,不會觸及磁盤
  • 強制安全:檢查異常(可能/可能),避免混合概念(Word,Int,Address)
  • 良好的數據結構(如拉鍊)可使某些類別的測試變得沒必要要,由於它們會靜態地排除例如越界錯誤。

剖析器

  • 提供程序的堆和時間配置文件的客觀證據。
  • 特別是堆分析是確保不使用沒必要要的內存的最佳方法。

純度

  • 經過刪除狀態顯着下降複雜性。 純功能代碼能夠擴展,由於它是組合的。 您須要的只是肯定如何使用某些代碼的類型 - 當您更改程序的其餘部分時,它不會神祕地破壞。
  • 使用大量的「模型/視圖/控制器」樣式編程:儘快將外部數據解析爲純函數數據結構,對這些結構進行操做,而後在完成全部工做後,渲染/刷新/序列化。 保持大部分代碼純淨

測試

  • QuickCheck + Haskell代碼覆蓋率,以確保您測試沒法檢查類型的內容。
  • GHC + RTS很適合看你是否花費太多時間作GC。
  • QuickCheck還能夠幫助您爲模塊識別乾淨,正交的API。 若是代碼的屬性很難說明,那麼它們可能過於複雜。 繼續重構,直到你擁有一組能夠測試代碼的完整屬性,這些屬性組合得很好。 那麼代碼也可能設計得很好。

Monads用於結構化

  • Monads以類型捕獲關鍵架構設計(此代碼訪問硬件,此代碼是單用戶會話等)
  • 例如,xmonad中的X monad,精確捕獲了系統的哪些組件可見的狀態設計。

鍵入類和存在類型

  • 使用類型類來提供抽象:隱藏多態接口背後的實現。

併發和並行

  • 潛行par到你的程序打,方便,可組合並行的競爭。

重構

  • 你能夠在Haskell中進行不少重構。 若是您明智地使用類型,這些類型可確保您的大規模更改是安全的。 這將有助於您的代碼庫擴展。 確保重構會致使類型錯誤,直到完成。

明智地使用FFI

  • FFI使得更容易使用外部代碼,但外國代碼可能很危險。
  • 關於返回的數據形狀的假設要很是當心。

元編程

  • 一些Template Haskell或泛型能夠刪除樣板。

包裝和分銷

  • 使用Cabal。 不要滾動本身的構建系統。 (編輯:其實你可能如今想要使用Stack開始。)
  • 使用Haddock得到優秀的API文檔
  • graphmod這樣的工具能夠顯示您的模塊結構。
  • 若是可能的話,依靠Haskell平臺版本的庫和工具。 這是一個穩定的基地。 (編輯:再次,這些天你可能想要使用Stack來得到穩定的基礎並運行。)

警告

  • 使用-Wall來保持代碼清潔氣味。 您還能夠查看Agda,Isabelle或Catch以得到更多保證。 對於相似lint的檢查,請參閱偉大的hlint ,它將提出改進建議。

使用全部這些工具,您能夠處理複雜性,儘量多地刪除組件之間的交互。 理想狀況下,你有一個很是大的純代碼基礎,它很容易維護,由於它是組合的。 這並不是老是可行,但值得瞄準。

一般: 將系統的邏輯單元分解爲可能的最小參考透明組件,而後在模塊中實現它們。 組件集(或組件內部)的全局或本地環境可能會映射到monad。 使用代數數據類型來描述核心數據結構。 普遍分享這些定義。


#4樓

在Haskell中設計大型程序與在其餘語言中進行設計沒有什麼不一樣。 大型編程是將您的問題分解爲可管理的部分,以及如何將這些部分組合在一塊兒; 實現語言不過重要。

也就是說,在大型設計中,嘗試利用類型系統以確保您只能以正確的方式將各個部分組合在一塊兒是一件好事。 這可能涉及newtype或phantom類型,以使看起來具備相同類型的東西不一樣。

當你進行重構代碼時,純度是一個很大的好處,因此儘可能保持儘量多的純代碼。 純代碼很容易重構,由於它與程序的其餘部分沒有隱藏的交互。


#5樓

Don給出了上面的大部分細節,但這是我在Haskell中執行系統守護進程等很是實用的有狀態程序時的兩分錢。

  1. 最後,你住在monad變換器堆棧中。 最底層是IO。 在此之上,每一個主要模塊(在抽象意義上,而不是文件中的模塊意義)將其必要狀態映射到該堆棧中的層。 所以,若是您將數據庫鏈接代碼隱藏在模塊中,則將其所有寫入MonadReader類型鏈接m => ... - > m ...而後您的數據庫函數始終能夠得到其鏈接而無需其餘函數模塊必須意識到它的存在。 您可能最終獲得一個承載數據庫鏈接的層,另外一個配置,第三個用於解決並行和同步的各類信號量和mvars,另外一個用於日誌文件處理等。

  2. 首先找出你的錯誤處理。 Haskell在大型系統中目前最大的弱點是過多的錯誤處理方法,包括像Maybe這樣糟糕的錯誤處理方法(這是錯誤的,由於你不能返回任何關於出錯的信息;老是使用Either而不是Maybe除非你真的只是意味着缺失值)。 弄清楚如何首先完成它,並從庫和其餘代碼使用的各類錯誤處理機制中設置適配器到最後一個。 這將爲您節省一個悲傷的世界。

附錄 (摘自評論;感謝Liiliminalisht ) -
更多關於將大型程序切割成堆棧中的monad的不一樣方法的討論:

Ben Kolera爲這個主題提供了一個很好的實用介紹, Brian Hurt討論了lift monadic動做lift到你的自定義monad的問題的解決方案。 George Wilson展現瞭如何使用mtl編寫適用於任何實現所需類型類的monad的代碼,而不是自定義monad類。 Carlo Hamalainen撰寫了一些簡短有用的筆記,總結了喬治的演講。

相關文章
相關標籤/搜索