当前使用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
将输入结束问题作为解析子集的原因。
我很好奇,这个协议如何与管道可组合性一起去?假设我有一个读取文件的管道'readFileK',并发送'Nothing'来表示结束。如果我做'(readFileK“file1”>> readFileK“file2”)> - > otherPipeK'那么'otherPipeK'会收到'Nothing'两次?另一方面,如果我有'readFileK'文件'>> - >(pipe1K >> pipe2K),并且当'pipe1K'正在处理时文件的输入被耗尽,那么'pipe2K'永远不会知道输入已经被耗尽。 – 2013-03-07 20:01:39
这就是为什么'onlyK'是一个单独的组合器,而'Nothing'行为没有内置到源中。这样你就可以将多个源合并为一个,比如'onlyK(readFileS“file”> => readSocketS socket)''。你的第二个例子不会导致任何问题。如果'pipe1K'输入完了,它将在'ParseP'中失败,'pipe2K'永远不会运行。没有一个解析原语能够越过输入标记的末尾。 – 2013-03-07 20:58:59