Каковы плюсы и минусы счетчиков против трубопроводов против труб?
Я хотел бы услышать от кого-то с более глубоким пониманием, чем я, каковы фундаментальные различия между счетчиками, кабелепроводами и трубами, а также основные преимущества и недостатки. Некоторое обсуждение уже продолжается, но было бы неплохо иметь обзор высокого уровня.
2 ответа
Перечислители / Итераторы как абстракция были придуманы Олегом Киселевым. Они обеспечивают чистый способ выполнения ввода-вывода с предсказуемыми (низкими) требованиями к ресурсам. Текущий пакет Enumerators довольно близок к оригинальной работе Олега.
Проводники были созданы для веб-фреймворка Yesod. Насколько я понимаю, они были разработаны, чтобы быть невероятно быстрым. Ранние версии библиотеки были очень полны состояния.
Трубы ориентированы на элегантность. Они имеют только один тип вместо нескольких, образуют экземпляры монад (преобразователей) и категорий и очень "функциональны" в дизайне.
Если вам нравятся категоричные объяснения: Pipe
type - это просто свободная монада над следующим безбожным простым функтором
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.
После написания приложений для всех трех библиотек, я думаю, самое большое различие, которое я видел, заключается в том, как обрабатывается финализация ресурсов. Например, Pipes разбивает финализацию ресурсов на отдельные типы фреймов и стеков.
Кажется, что до сих пор идут споры о том, как не только завершить ввод ресурсов, но и потенциально вывести ресурс. Например, если вы читаете из БД и записываете в файл, соединение с БД должно быть закрыто, а выходной файл должен быть очищен и закрыт. При решении вопроса о том, как обрабатывать исключения и случаи сбоев на конвейере, дела идут не так.
Другое, более тонкое различие заключается в том, как обрабатывается и вычисляется возвращаемое значение конвейера перечислителя.
Многие из этих различий и потенциальных несоответствий были выявлены при использовании реализаций Monad и Category для Pipes, и теперь они пробиваются в Conduits.