Каковы плюсы и минусы счетчиков против трубопроводов против труб?

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

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.

Другие вопросы по тегам