如何将水槽变成管道?

ehi*_*ird 7 haskell conduit

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

attoparsec-conduit提供sinkParser把a Parser变成a Sink,但我怎么能把它Sink变成a Conduit?我正在寻找的功能如下:

conduitSink :: (Resource m) => Sink a m b -> Conduit a m b
Run Code Online (Sandbox Code Playgroud)

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

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

dfl*_*str 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
Run Code Online (Sandbox Code Playgroud)

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

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

使用可用的ResourceTmonad接口,所需的接收器的实现应该是相当简单的.

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