如何将水槽变成管道?

时间:2012-01-28 04:10:09

标签: haskell conduit

我正在尝试使用attoparsec解析器编写Conduit。具体来说,给定parseOne :: Parser T,我想构造一个Conduit ByteString m T,重复将解析器应用于输入并流式传输结果。

attoparsec-conduit提供sinkParserParser变为Sink,但如何将此Sink变为Conduit?我正在寻找的功能如下:

conduitSink :: (Resource m) => Sink a m b -> Conduit a m b

重复将数据输入Sink,随着时间的推移生成每个结果。看起来它可以很容易地写成手动循环,但我想知道是否有更好的方法。

导管库中缺乏这个看似显而易见的功能让我觉得我可能做错了什么;有没有更好的方法来实现这一目标?用例是将原始字节转换为基于消息的网络协议的解析形式,由管道的后续阶段处理。由于blaze-builder-conduit,我已经有相反的方向(即Conduit T m ByteString),所以这似乎是构建事物的最自然的方式。

1 个答案:

答案 0 :(得分:6)

您需要使用SequencedSink系统;它使用水槽和跟踪状态来生成水槽生产者重复应用的管道。

您创建的接收器已经过优化,可以逐步解析一个值,这将是导管序列末尾的结果。

由于您希望将其作为管道管道的一部分,并且传入的ByteString的每个块可能会或可能不会与您的解析器匹配一次或多次,因此您需要注意使其更加精细 - 粗略控制解析过程,在接收器的每个应用程序之间传递不完整解析的状态。

假设,例如,您的解析器解析[--][----]等,而TInt表示解析的破折号数,则需要跟踪解析器的状态如下所示:

Input chunk    Sink result - Data.Conduit.SequencedSinkResponse
[--][---]      Emit Nothing [2, 3]
[---][---      Emit (Just #func) [3]
---------      Emit (Just #func) []
]              Emit Nothing [12]
               Stop

在这种情况下,我使用Maybe (ByteString -> Data.Attoparsec.ByteString.Result)作为传递状态;根据具体情况,不同的数据类型可能更合适。

需要这种明确的流处理来维持管道的管道性质;让解析器管道成为“瓶颈”,总是在等待足够数据来满足解析器,这将是一个主要的性能下沉。

使用可用的ResourceT monad接口,所需接收器的实现应该相当简单。

编辑:简单地在循环中应用您的接收器确实是最简单的解决方案,但如果您的解析器解析通常最终在字节块的边界上的短片段,它将具有稍微不同的性能特征