Как я могу превратить раковину в проводник?

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

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

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

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

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


person ehird    schedule 28.01.2012    source источник


Ответы (1)


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

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

Однако, поскольку вы хотите, чтобы это было частью конвейера канала, и каждый фрагмент входящего 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 монады.

EDIT: Простое применение вашего приемника в цикле действительно было бы самым простым решением, но оно будет иметь немного другие характеристики производительности, если ваш синтаксический анализатор анализирует короткие фрагменты, которые часто заканчиваются на границах фрагментов байтов.

person dflemstr    schedule 28.01.2012
comment
Спасибо, я попробую. Означает ли это, что я вообще не буду использовать attoparsec-conduit? Если да, то будут ли какие-либо препятствия для добавления универсального conduitParser :: (AttoparsecInput a, ResourceThrow m) => Parser a b -> Conduit a m b в его интерфейс с использованием этой техники, или это простое упущение? - person ehird; 28.01.2012
comment
@ehird, я считаю, что это просто упущение; текущий код sinkParser предполагает, что его можно легко преобразовать для многократного синтаксического анализа входного потока, поскольку он использует технику, аналогичную описанной выше, за исключением того, что он прекращает потреблять ввод после первого синтаксического анализа. - person dflemstr; 28.01.2012
comment
Действительно, похоже, уже есть запрос на включение, добавляющий conduitParser в attoparsec-enumerator; Я, вероятно, буду использовать эту реализацию. Кстати, спасибо, что сообщили мне о SequencedSink; Я пропустил это, когда читал документацию. - person ehird; 29.01.2012