2014-12-29 24 views
5

我正在嘗試從管道讀取最多50個項目的組,並且一次處理IO操作中的所有項目。 (這是用例,我試圖將數據插入到數據庫中,並且我想在一個事務中執行整個批處理,因爲它效率更高)。以下是對我這麼遠的簡化版本:如何使用管道檢測輸入的結尾

type ExampleType = Int 

doSomething :: [ExampleType] -> IO() 
doSomething = undefined 

inGroupsOf50 :: Monad m => Producer ExampleType m() -> m() 
inGroupsOf50 input = 
    runEffect $ input >-> loop 
     where loop = do entries <- replicateM 50 await 
        lift $ doSomething entries --Insert a bunch all in one transaction 
        loop 

的問題,據我所知,除非項目要插入的數目發生了50分,我會想念一些。我真正想要的,而不是replicateM 50 await是給我多達50個項目或更少,如果輸入結束,但我不知道如何寫這個。

我一直在想,pipes-parse可能是正確的圖書館。 draw看起來有一個很有希望的簽名......但到目前爲止,所有的東西都不適合我的腦袋。我有一個producer,我正在寫一個consumer,我真的不知道這與parser的概念有什麼關係。

回答

11

甚至超過pipes-parse你很可能想看看pipes-group。尤其是,讓我們來看看功能

-- this type is slightly specialized 
chunksOf 
    :: Monad m => 
    Int -> 
    Lens' (Producer a m x) (FreeT (Producer a m) m x) 

Lens'位也許是可怕的,但可以很快被淘汰:它指出我們可以轉換Producer a m xFreeT (Producer a m) m x [0]

import Control.Lens (view) 

chunkIt :: Monad m => Int -> Producer a m x -> FreeT (Producer a m) m x 
chunkIt n = view (chunksOf n) 

所以現在我們要弄清楚該怎麼做FreeT位。特別是,我們會想挖到free包並拉出功能iterT

iterT 
    :: (Functor f, Monad m) => 
    (f (m a) -> m a) -> 
    (FreeT f m a -> m a) 

此功能,iterT,讓我們「消費」一次一個FreeT一個「臺階」。要理解這一點,我們首先專注的iterT類型與Producer a m

runChunk :: Monad m => 
      (Producer a m (m x)  -> m x) -> 
      (FreeT (Producer a m) m x -> m x) 
runChunk = iterT 

特別是更換frunChunk可以在「運行」一FreeT充分的Producer這麼只要我們告訴它如何轉換Producer a m (m x)進入m -action。 可能會開始看起來更熟悉。當我們定義第一個參數runChunk時,我們只需執行一個Producer,在這種情況下,它將不會超過所選數量的元素。

但是,有效的返回值m x是怎麼回事?這是「延續」,例如之後之後的所有塊。所以,舉例來說,假設我們有CharProducer A S,我們希望經過3個字符

打印和換行符
main :: IO() 
main = flip runChunk (chunkIt 3 input) $ \p -> _ 

在這一點上_孔具有在上下文類型IO()p鍵入p :: Producer Char IO (IO())。我們可以使用for來使用這個管道,收集它的返回類型(這是繼續,再次),發出一個換行符,然後運行延續。

input :: Monad m => Producer Char m() 
input = each "abcdefghijklmnopqrstuvwxyz" 

main :: IO() 
main = flip runChunk (chunkIt 3 input) $ \p -> do 
    cont <- runEffect $ for p (lift . putChar) 
    putChar '\n' 
    cont 

,並根據需要

λ> main 
abc 
def 
ghi 
jkl 
mno 
pqr 
stu 
vwx 
yz 

要清楚,而我做了一些闡述的這種行爲完全,這是相當簡單的代碼,一旦你看到所有的部分是如何結合在一起的。以下是整個列表:

input :: Monad m => Producer Char m() 
input = each "abcdefghijklmnopqrstuvwxyz" 

main :: IO() 
main = flip iterT (input ^. chunksOf 3) $ \p -> do 
    cont <- runEffect $ for p $ \c -> do 
    lift (putChar c) 
    putChar '\n' 
    cont 

[0]還有一點,但現在已經足夠了。