枚举器、导管和管道的优缺点是什么?

34

4
管道和管子仍在经历大量修订,因此在它们稳定之前很难进行比较和对比。在理想情况下,管道和管子将合并成为正确处理枚举器的方法。 - Dan Burton
2个回答

29

迭代器/迭代器抽象化是由Oleg Kiselyov发明的。它们提供了一种干净的方法来进行IO操作,并具有可预测(低)的资源需求。目前的Enumerators软件包与Oleg最初的工作非常接近。

Conduits是为Yesod Web框架创建的。我的理解是它们被设计成非常快速。该库的早期版本高度依赖状态。

Pipes专注于优雅性。它们只有一种类型而不是多种,形成了单子(变换器)和类别实例,并且在设计上非常“函数化”。如果您喜欢范畴解释: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下形成一个类别,该操作将第一个管道yield的内容传送到等待的第二个管道中。
看起来像是Conduits正在变得更像Pipe(使用CPS代替状态,并转换为单一类型),而 Pipes 正在获得更好的错误处理支持,或许会恢复生成器和消费者的独立类型。
这个领域正在快速发展。我一直在试验 Pipe 库的一种变体,具备这些功能,并且知道其他人也在尝试(请参见 Hackage 上的 Guarded Pipes 包),但我怀疑 Gabriel(Pipes 的作者)会比我更早找到解决方案。
我的建议是:如果您使用 Yesod,请使用 Conduits。如果您需要成熟的库,请使用 Enumerator。如果您主要关心优雅性,请使用 Pipe。

7
在使用这三个库编写应用程序后,我认为最大的区别在于资源终止处理方式。例如,Pipes将资源终止分解成了不同类型的Frames和Stacks。
此外,仍然存在一些争议,如何不仅终止输入资源,还可能终止输出资源。例如,如果您从数据库中读取并写入文件,则需要关闭DB的连接以及刷新并关闭输出文件。在决定如何处理管道中的异常和故障情况时,事情变得复杂起来。
另一个更微妙的差异似乎是如何处理和计算枚举器管道的返回值。
许多这些差异和潜在的不一致性已经通过在Pipes中使用Monad和Category实现而暴露出来,现在正在被引入Conduits中。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接