2013-03-06 55 views
10

我想了解管道概念的不同實現之間的差異。 導管管道之間的區別之一是它們如何將管道熔合在一起。 管道管道上游類型參數的真正好處是什麼?

(>+>) :: Monad m 
     => Pipe l a b r0 m r1 -> Pipe Void b c r1 m r2 -> Pipe l a c r0 m r2 

(>->) :: (Monad m, Proxy p) 
     => (b' -> p a' a b' b m r) -> (c' -> p b' b c' c m r) -> c' -> p a' a c' c m r 

如果我理解正確的話,與管道,當兩個站的任何管道,則返回它的結果,另一種停止。通過導管,如果左側管道完成,其結果將發送到右側管道的下游。

我在想,導管的方法有什麼好處?我想看到一些使用導管>+>很容易實現的示例(最好是真實世界),但使用管道>->很難實現。

回答

5

根據我的經驗,上游端接器的真實優勢非常渺茫,這就是爲什麼他們在此時隱藏在公共API之下。我想我只在一段代碼中使用它們(wai-extra的多部分解析)。

在最通用的形式中,Pipe允許您生成輸出值流和最終結果。當您將該管道與另一個下游管道相融合時,則該輸出值流將成爲下游的輸入流,並且上游的最終結果將成爲下游的「上游終結器」。所以從這個角度來看,擁有任意上游終結符允許一個對稱的API。

但是,在實踐中,實際使用這種功能的情況非常罕見,並且由於它只是混淆了API,所以隱藏在1.0版本的.Internal模塊中。一個理論用例可能如下:

  • 你有一個源產生一個字節流。
  • Conduit消耗字節流,計算散列作爲最終結果,並傳遞所有下游字節。
  • 一個吸收字節流的宿節點,例如將它們存儲在一個文件中。

使用上游端接器,您可以將這三個端口連接起來,並將管道的結果作爲管道的最終結果返回。然而,在大多數情況下,實現相同目標的方法有一種更簡單的方法。在這種情況下,您可以:

  1. 使用conduitFile存儲在一個文件中的字節,並打開哈希管道分成散列水槽,並把它下游
  2. 使用zipSinks合併這兩個散列水槽和文件寫入陷入一個單一的水槽。
9

當前使用conduit更容易實現的一個經典示例是處理來自上游的輸入結束。例如,如果要摺疊值列表並在管道中綁定結果,則無法在pipes內完成此操作,而無需在pipes之上設計額外的協議。

實際上,這正是即將到來的pipes-parse庫解決的問題。它在pipes之上設計了一個Maybe協議,然後定義了方便的功能,用於從上游繪製關於該協議的輸入。

例如,你有onlyK函數,該函數的管幷包裝在Just所有輸出,然後結束與Nothing

onlyK :: (Monad m, Proxy p) => (q -> p a' a b' b m r) -> (q -> p a' a b' (Maybe b) m r) 

還具有justK功能,其定義從管函子這是Maybe -unaware到是Maybe知曉的向後兼容性

justK :: (Monad m, ListT p) => (q -> p x a x b m r) -> (q -> p x (Maybe a) x (Maybe b) m r) 

justK idT = idT 
justK (p1 >-> p2) = justK p1 >-> justK p2 

,然後一旦你有一個管道尊重該協議,您可以使用大量的解析器,通過Nothing檢查爲您抽象。最簡單的一個是draw

draw :: (Monad m, Proxy p) => Consumer (ParseP a p) (Maybe a) m a 

它檢索a類型的值或在ParseP代理變壓器失敗如果上游跑出輸入的。你也可以一次取多個值:

drawN :: (Monad m, Proxy p) => Int -> Consumer (ParseP a p) (Maybe a) m [a] 

drawN n = replicateM n draw -- except the actual implementation is faster 

...和其他幾個很好的功能。用戶從來不必直接與輸入信號的末端進行交互。

通常當人們要求輸入結束處理時,他們真正想要的是解析,這就是爲什麼pipes-parse將輸入結束問題作爲解析子集的原因。

+0

我很好奇,這個協議如何與管道可組合性一起去?假設我有一個讀取文件的管道'readFileK',併發送'Nothing'來表示結束。如果我做'(readFileK「file1」>> readFileK「file2」)> - > otherPipeK'那麼'otherPipeK'會收到'Nothing'兩次?另一方面,如果我有'readFileK'文件'>> - >(pipe1K >> pipe2K),並且當'pipe1K'正在處理時文件的輸入被耗盡,那麼'pipe2K'永遠不會知道輸入已經被耗盡。 – 2013-03-07 20:01:39

+0

這就是爲什麼'onlyK'是一個單獨的組合器,而'Nothing'行爲沒有內置到源中。這樣你就可以將多個源合併爲一個,比如'onlyK(readFileS「file」> => readSocketS socket)''。你的第二個例子不會導致任何問題。如果'pipe1K'輸入完了,它將在'ParseP'中失敗,'pipe2K'永遠不會運行。沒有一個解析原語能夠越過輸入標記的末尾。 – 2013-03-07 20:58:59