导管上游类型参数的真正好处是什么?

Pet*_*lák 10 haskell conduit haskell-pipes

我试图理解管道概念的不同实现之间的差异.导管管道之间的区别之一是它们如何将管道熔合在一起.管道

(>+>) :: Monad m
      => Pipe l a b r0 m r1 -> Pipe Void b c r1 m r2 -> Pipe l a c r0 m r2
Run Code Online (Sandbox Code Playgroud)

(>->) :: (Monad m, Proxy p)
      => (b' -> p a' a b' b m r) -> (c' -> p b' b c' c m r) -> c' -> p a' a c' c m r
Run Code Online (Sandbox Code Playgroud)

如果我理解正确,使用管道,当两个管道的任何管道停止时,返回其结果而停止另一个.使用导管,如果左侧管道完成,其结果将向下游发送到右侧管道.

我想知道,管道方法的好处是什么?我想看到一些例子(最好是真实世界),这是很容易使用来实现管道>+>,但硬(ER)使用来实现管道>->.

Gab*_*lez 9

conduit目前更容易实现的经典示例是处理来自上游的输入结束.例如,如果要折叠值列表并将结果绑定到管道中,则无法在pipes不设计额外协议的情况下执行此操作pipes.

事实上,这正是即将到来的pipes-parse图书馆所解决的问题.它在一个Maybe协议之上设计了一个协议pipes,然后定义了方便的功能,用于从上游绘制来自该协议的输入.

例如,你有一个onlyK函数,它接受一个管道并包装所有输出Just,然后用一个完成Nothing:

onlyK :: (Monad m, Proxy p) => (q -> p a' a b' b m r) -> (q -> p a' a b' (Maybe b) m r)
Run Code Online (Sandbox Code Playgroud)

您还拥有该justK函数,该函数定义了一个来自管道的Maybe仿函数,这些管道是管道,它们Maybe具有向后兼容性

justK :: (Monad m, ListT p) => (q -> p x a x b m r) -> (q -> p x (Maybe a) x (Maybe b) m r)

justK idT = idT
justK (p1 >-> p2) = justK p1 >-> justK p2
Run Code Online (Sandbox Code Playgroud)

然后,一旦你有一个Producer尊重该协议,你可以使用各种解析器抽象Nothing检查你.最简单的是draw:

draw :: (Monad m, Proxy p) => Consumer (ParseP a p) (Maybe a) m a
Run Code Online (Sandbox Code Playgroud)

如果上游用完输入a,它会在ParseP代理转换器中检索类型或失败的值.您还可以一次获取多个值:

drawN :: (Monad m, Proxy p) => Int -> Consumer (ParseP a p) (Maybe a) m [a]

drawN n = replicateM n draw  -- except the actual implementation is faster
Run Code Online (Sandbox Code Playgroud)

......还有其他一些不错的功能.用户实际上根本不必直接与输入信号的末端交互.

通常当人们要求输入结束处理时,他们真正想要的是解析,这就是pipes-parse将输入结束问题作为解析子集的原因.


Mic*_*man 5

根据我的经验,上游终结器的实际好处非常渺茫,这就是为什么它们此时隐藏在公共API之外.我想我只在一段代码中使用它们(wai-extra的多部分解析).

在最常见的形式中,管道允许您生成输出值流和最终结果.当您将该管道与另一个下游管道融合时,该输出值流成为下游的输入流,而上游的最终结果成为下游的"上游终结器".因此,从这个角度来看,具有任意上游终结符允许对称API.

但是,在实践中,实际使用这种功能的情况非常罕见,并且由于它只是混淆了API,因此它被隐藏在1.0版本的.Internal模块中.一个理论用例可能如下:

  • 你有一个产生字节流的Source.
  • 一个使用字节流的Conduit,计算一个哈希作为最终结果,并传递下游的所有字节.
  • 消耗字节流的接收器,例如,将它们存储在文件中.

使用上游终结器,您可以将这三个终结器连接起来,并将管道返回的结果作为管道的最终结果返回.但是,在大多数情况下,有一种替代的,更简单的方法来实现相同的目标.在这种情况下,您可以:

  1. 使用conduitFile的字节存储在一个文件,并打开哈希管道分成散列水槽和下游放置
  2. 使用zipSinks将散列接收器和文件写入接收器合并到一个接收器中.