将管道合并为一体

时间:2016-02-17 11:07:06

标签: haskell conduit

我正在寻找一个可以做类似于:

的功能
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之类的事情。有可能吗?

2 个答案:

答案 0 :(得分:3)

查看ZipSource,这是一个新类型包装器,Applicative允许您按照自己想要的方式组合Source

获得ZipSource后,您可以使用zipSourcesSource中的Traversable(例如列表)合并到Source Traversable秒。

与您期望的结果类型的唯一区别在于它是Source超过Traversable的值,而不仅仅是单个值,但这不应该是一个问题

答案 1 :(得分:1)

mergeSources中的{p> stm-conduit在场景后面保持TBMChannel。您的所有源/生产者首先连接到TBMChannel,然后它将创建一个源,尝试从通道FIFO中拉出值。

使用TBMChannel时,您可以设置中间mergeSources的界限。假设您将绑定设置为n,那么所有源生成的前n个值将立即转储到TBMChannelAmqpConn,假设它未在AmqpConn端被阻止,并且您的消费者比来源慢(BTW AmqpConn使用无界Control.Concurrent.Chan因此它不会阻止)。之后TBMChannel已满,因此阻止了尝试为通道生成值的源。您的消费者从组合源中逐个获取值,因此它在前n个元素之后是连续的。

要确保它从头开始是连续的,您可以将绑定设置为1,但这可能会导致一些性能问题。