Классический пример чего-то более легкого в использовании с conduit
в настоящее время обрабатывает конец ввода от восходящего потока. Например, если вы хотите сбросить список значений и связать результат в конвейере, вы не сможете сделать это в пределах pipes
без разработки дополнительного протокола поверх pipes
.
Фактически, это именно то, что решает новая библиотека pipes-parse
. Он разрабатывает протокол Maybe
поверх pipes
, а затем определяет удобные функции для ввода входных данных с восходящего потока, которые уважают этот протокол.
Например, имеет функцию onlyK
, которая принимает трубку и оборачивает все выходы в Just
, а затем заканчивает с Nothing
:
onlyK :: (Monad m, Proxy p) => (q -> p a' a b' b m r) -> (q -> p a' a b' (Maybe b) m r)
Вы также иметь функцию justK
, которая определяет функтор из труб которые являются Maybe
-unaware к трубам, которые Maybe
-aware для обратной совместимости
justK :: (Monad m, ListT p) => (q -> p x a x b m r) -> (q -> p x (Maybe a) x (Maybe b) m r)
justK idT = idT
justK (p1 >-> p2) = justK p1 >-> justK p2
И затем, как только у вас есть Producer
, который уважает этот протокол, вы можете использовать большое разнообразие парсеров, которые аннотация над Nothing
проверит вас. Простым является draw
:
draw :: (Monad m, Proxy p) => Consumer (ParseP a p) (Maybe a) m a
Он извлекает значение типа a
или не в ParseP
прокси трансформатора, если вверх по течению выбежала ввода. Вы также можете взять сразу несколько значений:
drawN :: (Monad m, Proxy p) => Int -> Consumer (ParseP a p) (Maybe a) m [a]
drawN n = replicateM n draw -- except the actual implementation is faster
... и несколько других приятных функций. Пользователь никогда не должен напрямую взаимодействовать с концом входного сигнала.
Обычно, когда люди запрашивают обработку ввода в конце ввода, то, что они действительно хотели, это синтаксический анализ, поэтому pipes-parse
обращается к проблемам ввода-вывода в качестве подмножества синтаксического анализа.
Мне любопытно, как этот протокол идет вместе с композицией труб? Предположим, что у меня есть труба 'readFileK', которая читает файл и посылает' Nothing', чтобы сигнализировать о завершении. Если я делаю '(readFileK" file1 ">> readFileK" file2 ")> -> otherPipeK', то' otherPipeK' получает 'Nothing' дважды? С другой стороны, если у меня есть файл readFileK''> -> (pipe1K >> pipe2K), и вход из файла исчерпан, а 'pipe1K' обрабатывается, тогда' pipe2K' никогда не узнает, что вход уже истощены. –
Вот почему 'onlyK' - это отдельный комбинатор, а поведение' Nothing' не встроено в источники. Таким образом вы можете объединить несколько источников в один, например 'onlyK (readFileS" file "> => readSocketS socket). Второй пример не вызывает никаких проблем. Если 'pipe1K' заканчивается, он не будет работать в' ParseP', а 'pipe2K' никогда не будет запущен. Ни один из примитивов синтаксического анализа не может пройти мимо конца входного маркера. –