我正在尝试从一个管道中读取一组最多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
而不是draw
,如果输入结束,我可以提供多达50个或更少的项目,但我无法弄清楚如何编写它。
我一直认为pipes-parse可能是正确的库。 producer
看起来有一个很有希望的签名......但到目前为止,所有的比特都没有融入我的脑海。我有一个consumer
,我正在写一个parser
而我并没有真正理解这与{{1}}的概念有什么关系。
答案 0 :(得分: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 x
转换为FreeT (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
的类型专门化为f
替换Producer a m
runChunk :: Monad m =>
(Producer a m (m x) -> m x) ->
(FreeT (Producer a m) m x -> m x)
runChunk = iterT
特别是,runChunk
可以“运行”FreeT
个Producer
个Producer a m (m x)
只要我们告诉它如何将m
转换为runChunk
}-行动。 这个可能会开始变得更加熟悉。当我们定义Producer
的第一个参数时,我们只需要执行一个m x
,在这种情况下,它将只有选定数量的元素。
但是有效的回报值Producer
是怎么回事?这是“延续”,例如在当前正在编写的那些块之后的所有块。所以,举个例子,我们假设我们有Char
main :: IO ()
main = flip runChunk (chunkIt 3 input) $ \p -> _
个,我们想在3个字符后打印和换行
_
此时IO ()
洞的类型为p
的{{1}}类型为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]还有点多,但现在已足够了。