34

我想听听比我自己更深入了解EnumeratorsConduitsPipes之间的根本区别以及主要优点和缺点的人。一些讨论已经 在进行中,但如果有一个高层次的概述会很好。

4

2 回答 2

28

枚举器/迭代器作为抽象是由 Oleg Kiselyov 发明的。它们提供了一种干净的方式来执行具有可预测(低)资源要求的 IO。当前的 Enumerators 包非常接近 Oleg 的原始作品。

管道是为 Yesod Web 框架创建的。我的理解是,它们的设计速度非常快。该库的早期版本是高度有状态的。

管道专注于优雅。它们只有一种类型而不是多种类型,形成 monad(变压器)和类别实例,并且在设计上非常“实用”。

如果你喜欢分类解释: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 库的实验变体,并且知道其他人也是如此(请参阅 Hackage 上的 Guarded Pipes 包),但怀疑 Gabriel(Pipes 的作者)会在我之前弄清楚它们做。

我的建议:如果您使用 Yesod,请使用 Conduits。如果您想要一个成熟的库,请使用 Enumerator。如果您主要关心优雅,请使用 Pipe。

于 2012-04-03T01:45:54.963 回答
7

在使用所有三个库编写应用程序之后,我认为我看到的最大区别在于资源最终确定的处理方式。例如,管道将资源终结分解为不同类型的帧和堆栈。

关于如何最终确定输入资源以及潜在的输出资源,似乎仍然存在一些争论。例如,如果您正在读取数据库并写入文件,则需要关闭数据库的连接以及需要刷新和关闭输出文件。在决定如何处理管道中的异常和失败案例时,事情变得很棘手。

另一个更细微的区别似乎是枚举器管道的返回值是如何处理和计算的。

许多这些差异和潜在的不一致已经通过使用管道的 Monad 和 Category 实现暴露出来,现在正在进入 Conduits。

于 2012-07-16T15:35:16.783 回答