我想听听比我更深刻理解的人Enumerators,Conduits和Pipes之间的根本区别以及主要的好处和缺点。一些discussion's already ongoing但是有一个高级概述会很高兴。
答案 0 :(得分:28)
为Yesod Web框架创建了管道。我的理解是它们的设计速度非常快。该图书馆的早期版本非常有状态。
管道专注于优雅。它们只有一种类型而不是几种,形成monad(变换器)和类别实例,并且在设计中非常“功能”。
如果您喜欢分类解释:Pipe
类型只是免费的monad而不是以下不敬虔的简单仿函数
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正在获得对更好的错误处理的支持,也许返回发电机和消费者的不同类型。
这个地区正在迅速发展。我一直在使用具有这些功能的管道库的实验变体进行攻击,并且知道其他人也是如此(请参阅Hackage上的Guarded Pipes包),但怀疑Gabriel(Pipes的作者)会在我之前解决它们做。
我的建议:如果您使用Yesod,请使用Conduits。如果您想要一个成熟的库,请使用Enumerator。如果您主要关心优雅,请使用Pipe。
答案 1 :(得分:7)
在用所有三个库编写应用程序之后,我认为我看到的最大区别在于如何处理资源最终化。例如,Pipes将资源最终化分解为不同类型的Frames和Stacks。
关于如何不仅最终确定输入资源,而且可能还有输出资源,似乎还存在一些争论。例如,如果您正在从DB读取并写入文件,则需要关闭DB的连接以及需要刷新和关闭的输出文件。在决定如何处理管道中的异常和故障情况时,事情会变得很糟糕。
另一个更微妙的区别似乎是如何处理和计算枚举器管道的返回值。
许多这些差异和潜在的不一致性已经通过使用管道的Monad和类别实现而暴露出来,现在正在进入管道。