2013-03-06 36 views
10

我想了解管道概念的不同实现之间的差异。 导管管道之间的区别之一是它们如何将管道熔合在一起。 管道管道上游类型参数的真正好处是什么?

(>+>) :: Monad m 
     => Pipe l a b r0 m r1 -> Pipe Void b c r1 m r2 -> Pipe l a c r0 m r2 

(>->) :: (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 

如果我理解正确的话,与管道,当两个站的任何管道,则返回它的结果,另一种停止。通过导管,如果左侧管道完成,其结果将发送到右侧管道的下游。

我在想,导管的方法有什么好处?我想看到一些使用导管>+>很容易实现的示例(最好是真实世界),但使用管道>->很难实现。

回答

5

根据我的经验,上游端接器的真实优势非常渺茫,这就是为什么他们在此时隐藏在公共API之下。我想我只在一段代码中使用它们(wai-extra的多部分解析)。

在最通用的形式中,Pipe允许您生成输出值流和最终结果。当您将该管道与另一个下游管道相融合时,则该输出值流将成为下游的输入流,并且上游的最终结果将成为下游的“上游终结器”。所以从这个角度来看,拥有任意上游终结符允许一个对称的API。

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

  • 你有一个源产生一个字节流。
  • Conduit消耗字节流,计算散列作为最终结果,并传递所有下游字节。
  • 一个吸收字节流的宿节点,例如将它们存储在一个文件中。

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

  1. 使用conduitFile存储在一个文件中的字节,并打开哈希管道分成散列水槽,并把它下游
  2. 使用zipSinks合并这两个散列水槽和文件写入陷入一个单一的水槽。
9

当前使用conduit更容易实现的一个经典示例是处理来自上游的输入结束。例如,如果要折叠值列表并在管道中绑定结果,则无法在pipes内完成此操作,而无需在pipes之上设计额外的协议。

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

例如,你有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) 

还具有justK功能,其定义从管函子这是Maybe -unaware到是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 

,然后一旦你有一个管道尊重该协议,您可以使用大量的解析器,通过Nothing检查为您抽象。最简单的一个是draw

draw :: (Monad m, Proxy p) => Consumer (ParseP a p) (Maybe a) m a 

它检索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 

...和其他几个很好的功能。用户从来不必直接与输入信号的末端进行交互。

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

+0

我很好奇,这个协议如何与管道可组合性一起去?假设我有一个读取文件的管道'readFileK',并发送'Nothing'来表示结束。如果我做'(readFileK“file1”>> readFileK“file2”)> - > otherPipeK'那么'otherPipeK'会收到'Nothing'两次?另一方面,如果我有'readFileK'文件'>> - >(pipe1K >> pipe2K),并且当'pipe1K'正在处理时文件的输入被耗尽,那么'pipe2K'永远不会知道输入已经被耗尽。 – 2013-03-07 20:01:39

+0

这就是为什么'onlyK'是一个单独的组合器,而'Nothing'行为没有内置到源中。这样你就可以将多个源合并为一个,比如'onlyK(readFileS“file”> => readSocketS socket)''。你的第二个例子不会导致任何问题。如果'pipe1K'输入完了,它将在'ParseP'中失败,'pipe2K'永远不会运行。没有一个解析原语能够越过输入标记的末尾。 – 2013-03-07 20:58:59