簡體   English   中英

是什么讓Iteratees值得復雜?

[英]What makes Iteratees worth the complexity?

首先,我理解iteratees的怎么樣 ,不夠好,我大概可以寫一個簡單的和越野車實現無再參考任何現有的。

我真正想知道的是為什么人們似乎發現它們如此迷人,或者在什么情況下它們的好處證明了它們的復雜性。 將它們與懶惰的I / O進行比較有一個非常明顯的好處,但這對我來說似乎非常像一個稻草人。 我從來沒有對懶惰的I / O感到滿意,除了偶爾的hGetContentsreadFile之外我都避免使用它,主要是在非常簡單的程序中。

在實際場景中,我通常使用傳統的I / O接口和適合任務的控件抽象。 在那種情況下,我只是沒有看到迭代者的好處,或者他們是一個適當的控制抽象的任務。 大多數時候,他們看起來更像是不必要的復雜性,甚至是適得其反的控制倒置。

我已經閱讀了很多關於它們的文章和使用它們的資料,但還沒有找到一個令人信服的例子,實際上讓我想到了“哦,是的,我也曾在那里使用它們“。 也許我只是沒有讀過正確的。 或許還有一個尚未設計的界面,比我見過的任何一個都簡單,這會使他們感覺不像瑞士軍用鏈鋸。

我只是患有非發明的綜合症或者我的不安是否有充分根據? 或者它可能完全不同於其他東西?

至於為什么人們覺得它們如此迷人,我認為因為它們是如此簡單的想法。 最近關於Haskell-cafe關於迭代的指稱語義的討論轉變為一種共識,即它們非常簡單,幾乎不值得描述。 短語“只有一個帶有暫停按鈕的美化左手折疊”從那個帖子向我伸出。 喜歡Haskell的人傾向於喜歡簡單,優雅的結構,所以迭代的想法可能非常吸引人。

對我來說,迭代的主要好處是

  1. 組合性。 不僅可以組成迭代,而且枚舉器也可以。 這非常強大。
  2. 安全的資源使用。 資源(主要是內存和句柄)無法逃避其本地范圍。 與嚴格的I / O相比,通過不清理更容易造成空間泄漏。
  3. 高效。 Iteratees可以高效; 與懶惰I / O和嚴格I / O競爭或更好。

我發現迭代器在處理來自多個源的單個邏輯數據時提供了最大的好處。 這是可組合性最有用的,並且具有嚴格I / O的資源管理最煩人(例如嵌套allocabracket )。

例如,在正在進行中的音頻編輯器中,單個邏輯聲音數據塊是一組偏移到多個音頻文件中的。 我可以通過做這樣的事情處理那一小塊聲音(從記憶中,但我認為這是正確的):

enumSound :: MonadIO m => Sound -> Enumerator s m a
enumSound snd = foldr (>=>) enumEof . map enumFile $ sndFiles snd

這對我來說似乎清晰,簡潔,優雅,遠遠超過了同等嚴格的I / O. Iteratees也足夠強大,可以包含我想要做的任何處理,包括寫輸出,所以我發現這非常好。 如果我使用懶惰的I / OI可以獲得優雅的東西,但要特別注意確保資源被消耗並且GC會超過IMO的優勢。

我也喜歡你需要在迭代中明確地保留數據,這避免了臭名昭着的mean xs = sum xs / length xs space leak。

當然,我不會將迭代用於一切。 作為一種替代方案,我非常喜歡with* idiom,但是當你有多個需要嵌套的資源時,它會很快變得復雜。

從本質上講,它是關於正確有效地執行功能樣式的 IO。 這就是全部,真的。

使用具有嚴格IO的准命令式風格,可以輕松實現正確和高效。 懶惰IO的功能風格很容易,但它在技術上是作弊(在引擎蓋下使用unsafeInterleaveIO ),並且可能存在資源管理和效率方面的問題。

非常非常通用的術語,許多純函數代碼遵循一種獲取數據的模式,遞歸地將其擴展為更小的片段,以某種方式轉換片段,然后將其重新組合成最終結果。 該結構可以是隱式的(在程序的調用圖中)或遍歷的顯式數據結構。

但是當IO涉及時,這就會崩潰。 假設您的初始數據是文件句柄,“遞歸擴展”步驟正在從中讀取一行,並且您無法立即將整個文件讀入內存。 這會強制在讀取下一行之前對每一行執行整個讀取 - 轉換 - 重組過程,因此,不使用干凈的“展開,映射,折疊”結構,而是使用嚴格的IO將它們混合成明確的遞歸monadic函數。

迭代器提供了一種替代結構來解決同樣的問題。 提取“變換和重新組合”步驟,而不是作為函數 ,將其改變為表示計算的當前狀態的數據結構 “遞歸擴展”步驟負責獲得數據並將其提供給(否則是被動的)迭代。

這提供了什么好處? 除其他事項外:

  • 因為iteratee是執行計算的單個步驟的被動對象,所以它們可以以不同的方式輕松組合 - 例如,交錯兩個迭代而不是順序運行它們。
  • 迭代器和枚舉器之間的接口是純粹的,只是正在處理的值流,因此純函數可以在它們之間自由拼接。
  • 數據源和計算忽略了彼此的內部工作,將輸入和資源管理與處理和輸出分離。

最終結果是程序可以具有更接近純功能版本的高級結構,具有許多與組合性相同的好處,同時具有與更強制性的嚴格IO版本相當的效率。

至於“值得復雜”嗎? 嗯,這就是事情 - 他們真的不是那么復雜,只是有點新奇和陌生。 這個想法一直在流動,幾年,幾年? 當人們在較大的項目中使用基於iteratee的IO(例如,使用Snap之類的東西)以及更多的示例/教程出現時,給它一些時間來擺脫困境。 事后看來,當前的實現可能在邊緣看起來非常粗糙。


有點相關:您可能想閱讀有關功能樣式IO的討論 Iteratees沒有被提及太多,但核心問題非常相似。 特別是這個解決方案 ,它非常優雅,甚至比抽象增量IO中的迭代更進一步

在什么情況下他們的利益證明了他們的復雜性

每種語言都有嚴格(經典)的IO,其中所有資源都由用戶管理。 Haskell還提供無處不在的惰性IO,其中所有資源管理都委托給系統。

但是,這可能會產生問題,因為資源范圍取決於運行時需求屬性。

Iteratees第三種方式:

  • 高級抽象,如懶惰的IO。
  • 顯式的,詞匯式的資源范圍,如嚴格的IO。

當您具有復雜的IO處理任務時,這是合理的,但資源使用的界限非常緊張。 一個例子是Web服務器。

實際上, Snap是圍繞epoll上的iteratee IO構建的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM