2012-04-02 6 views
33

Хотелось бы услышать от кого-то с более глубоким пониманием, чем я, основные различия между Enumerators, Conduits и Pipes, а также основные преимущества и недостатки. Некоторые discussion's alreadyongoing, но было бы неплохо иметь обзор высокого уровня.Каковы плюсы и минусы Enumerators против Conduits vs. Pipes?

+4

Трубы и трубы по-прежнему подвергаются серьезной ревизии, поэтому их трудно сравнивать и контрастировать, пока они не успокоятся. В идеальной ситуации трубопроводы и трубы сливаются в «Правильный способ делать перечислители». –

ответ

28

Перечислители/Итераты как абстракция были изобретены Олегом Киселевым. Они обеспечивают чистый способ ввода 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 вместо состояния, и переход к одному типу), а трубы, получают поддержку для лучшей обработки ошибок, и, возможно, возвращение отдельных типов генераторов и потребителей ,

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

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

7

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

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

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

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