Подтвердить что ты не робот

Каковы плюсы и минусы Enumerators против Conduits vs. Pipes?

Я хотел бы услышать от кого-то с более глубоким пониманием, чем я, что фундаментальные различия между Enumerators, Conduits и Pipes, а также основные преимущества и недостатки. Некоторая дискуссия уже продолжалась, но было бы неплохо иметь обзор высокого уровня.

4b9b3361

Ответ 1

Перечислители/Iteratees как абстракция были изобретены Олегом Киселевым. Они обеспечивают чистый способ ввода IO с предсказуемыми (низкими) требованиями к ресурсам. Текущий пакет Enumerators довольно близок к оригинальной работе Олега.

Каналы были созданы для веб-фреймворка Yesod. Я понимаю, что они были разработаны, чтобы быть невероятно быстрыми. Ранние версии библиотеки были высококонкурентными.

Трубы сосредотачиваются на элегантности. Они имеют только один тип вместо нескольких, формируют монады (трансформатор) и экземпляры категорий и очень "функциональны" в дизайне.

Если вам нравятся категорические объяснения: тип Pipe - это просто свободная монада над следующим нечестивым простым функтором

data PipeF a b m r = M (m r) | Await (a -> r) | Yield b r
instance Monad m => Functor (PipeF a b m) where
   fmap f (M mr) = M $ liftM mr
   fmap f (Await g) = Await $ f . g
   fmap f (Yield b p) = Yield b (f p)
--Giving:
newtype Pipe a b m r = Pipe {unPipe :: Free (PipeF a b m) r}
  deriving (Functor, Applicative, Monad)

--and
instance MonadTrans (Pipe a b) where
   lift = Pipe . inj . M

В фактическом определении трубы они запекаются, но простота этого определения поразительна. Трубы образуют категорию под операцией (<+<) :: Monad m => Pipe c d m r -> Pipe a b m r -> Pipe a d m r, которая берет любой первый трубопровод yields и подает его на ожидающий второй трубопровод.

Похоже, что Conduits движется больше Pipe как (используя CPS вместо состояния и переключаясь на один тип), в то время как Pipes получают поддержку для лучшей обработки ошибок и, возможно, возврат отдельных типов для генераторов и потребителей.

Эта область быстро перемещается. Я взломал экспериментальный вариант библиотеки Pipe с этими функциями и знаю других людей (см. Пакет Guarded Pipes на Hackage), но подозреваю, что Габриэль (автор Pipes) выяснит их до того, как я делать.

Мои рекомендации: если вы используете Yesod, используйте Conduits. Если вы хотите, чтобы зрелая библиотека использовала Enumerator. Если вы в первую очередь заботитесь об элегантности, используйте Pipe.

Ответ 2

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

Также, по-прежнему, есть некоторые дискуссии о том, как не только завершить входной ресурс, но и потенциально использовать выходной ресурс. Например, если вы читаете из БД и записываете в файл, соединение для БД должно быть закрыто, а также выходной файл, который нужно очистить и закрыть. Вещи становятся волосатыми, когда решаются способы обработки исключений и сбоев по трубопроводу.

Еще одна более тонкая разница заключается в том, как обрабатывается и вычисляется возвращаемое значение конвейера перечислителя.

Многие из этих различий и потенциальных несоответствий были выявлены благодаря использованию реализаций Monad и Category для Pipes и теперь проникают в Conduits.