Как я могу превратить мойку в канал? - PullRequest
7 голосов
/ 28 января 2012

Я пытаюсь написать Conduit, используя attoparsec парсер. В частности, учитывая parseOne :: Parser T, я хотел бы создать Conduit ByteString m T, который неоднократно применяет синтаксический анализатор к входным данным и передает результаты.

attoparsec-проводник предлагает sinkParser превратить Parser в Sink, но как я могу превратить этот Sink в Conduit? То, что я ищу, это такая функция:

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

, который многократно подает данные в Sink, производя каждый результат по мере поступления. Кажется, что это может быть написано довольно просто как ручной цикл, но мне интересно, есть ли лучший способ.

Отсутствие этой, казалось бы, очевидной функции в библиотеке каналов заставляет меня думать, что я могу делать что-то не так; Есть ли лучший способ сделать это? Вариант использования - преобразование необработанных байтов в разобранную форму сетевого протокола на основе сообщений для обработки на более поздних этапах конвейера. У меня уже есть противоположное направление (то есть Conduit T m ByteString) благодаря блейз-строителю-трубопроводу , так что это казалось наиболее естественным способом структурирования вещей.

1 Ответ

6 голосов
/ 28 января 2012

Для этого вам необходимо использовать систему SequencedSink; он использует приемник и отслеживаемое состояние для создания канала от многократного применения производителя приемника.

Созданный вами приемник оптимизирован для пошагового анализа значения one , которое будет результатом в конце последовательности каналов.

Однако, поскольку вы хотите, чтобы это было частью конвейерного конвейера, и каждый кусок входящего ByteString мог бы или не мог бы соответствовать вашему парсеру один или несколько раз, вам нужно позаботиться о том, чтобы получить более детальный контроль процесса синтаксического анализа, передавая состояние неполного разбора между каждым применением приемника.

Предполагая, например, что ваш синтаксический анализатор анализирует [--] или [----] и т. Д., А T равен Int, обозначая количество проанализированных дефисов, вам необходимо отслеживать состояние синтаксического анализатора, как показано этим :

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 интерфейсом монады.

РЕДАКТИРОВАТЬ: Простое применение приемника в цикле действительно было бы самым простым решением, но оно будет иметь несколько иные характеристики производительности, если ваш анализатор будет анализировать короткие фрагменты, которые часто оказываются на границах блоков байтов.

...