我正在寻找一个可以做类似于:
的功能merge :: MonadIO m => [Producer m a] -> Producer m a
我快速查看stm-conduit
,它看起来很相似,但我不确定它是否符合我的要求:
messagesSource :: MonadIO m => AmqpConn -> Ack -> Text -> Producer m (Message, Envelope)
messagesSource conn ack q = loop
where
loop = do
mmsg <- liftIO $ getMsg chan ack q
case mmsg of
Just (m, e) -> do
yield (m, e)
liftIO $ ackMsg chan (envDeliveryTag e) False
loop
Nothing -> loop
chan = fst $ amqpChan conn
正如您所看到的,此管道生产者在产生它之后会收到一条消息。在一个简单的“单线程”管道中它运行良好,消息进入接收器然后被激活。
然而,对于stm-conduit
,这可能会发生变化,因为据我所知,生产者不会等待信息被接收器消耗,他们会并行工作,而且可能会过早地收到消息。
我对stm-conduit
的理解是否正确?
那么将单独的源合并为一个具有良好的单流语义的方法是什么?
更新:根据要求更新了实际工作AMQP示例的代码(但可能有点吵闹)。
更新2 :我认为我所追求的可能是管道来源的替代实例,所以我可以做let src = src1 <|> src2
之类的事情。有可能吗?
答案 0 :(得分:3)
查看ZipSource
,这是一个新类型包装器,Applicative
允许您按照自己想要的方式组合Source
。
获得ZipSource
后,您可以使用zipSources
将Source
中的Traversable
(例如列表)合并到Source
Traversable
秒。
与您期望的结果类型的唯一区别在于它是Source
超过Traversable
的值,而不仅仅是单个值,但这不应该是一个问题
答案 1 :(得分:1)
mergeSources
中的{p> stm-conduit
在场景后面保持TBMChannel
。您的所有源/生产者首先连接到TBMChannel
,然后它将创建一个源,尝试从通道FIFO中拉出值。
使用TBMChannel
时,您可以设置中间mergeSources
的界限。假设您将绑定设置为n,那么所有源生成的前n个值将立即转储到TBMChannel
和AmqpConn
,假设它未在AmqpConn
端被阻止,并且您的消费者比来源慢(BTW AmqpConn
使用无界Control.Concurrent.Chan
因此它不会阻止)。之后TBMChannel已满,因此阻止了尝试为通道生成值的源。您的消费者从组合源中逐个获取值,因此它在前n个元素之后是连续的。
要确保它从头开始是连续的,您可以将绑定设置为1,但这可能会导致一些性能问题。