导管残羹剩饭的好处是什么?

时间:2013-03-06 21:38:30

标签: haskell conduit haskell-pipes

我正在尝试了解 conduit 管道之间的区别。与管道不同,管道具有剩余的概念。什么是剩菜有用?我想看一些残羹剩饭必不可少的例子。

由于管道没有剩余的概念,有没有办法与它们达成类似的行为?

2 个答案:

答案 0 :(得分:17)

加布里埃尔认为,剩菜总是解析的一部分很有意思。我不确定我会同意,但这可能只取决于解析的定义。

有大量用例需要剩菜。解析肯定是一个:任何时候解析都需要某种前瞻,你需要剩下的东西。其中一个例子是markdown包的getIndented函数,它将所有即将到来的行隔离一定的缩进级别,剩下的行将在稍后处理。

但是更多平凡的例子存在于管道本身。无论何时处理打包数据(如ByteString或Text),您都需要读取一个块,以某种方式分析它,使用剩余的来推回额外的数据,然后对原始内容执行某些操作。也许最简单的例子是dropWhile

事实上,我认为leftover是流媒体库的核心基本功能,管道的新1.0接口甚至不会向禁用剩余的用户公开选项。我知道很少有真实世界的用例不需要这种或那种方式。

答案 1 :(得分:16)

我会回答pipes。对你的问题的简短回答是,即将到来的pipes-parse库将支持剩余部分,作为更通用的解析框架的一部分。我发现几乎每个人都想要剩菜的情况下他们实际上都想要一个解析器,这就是为什么我将剩余问题作为解析的一个子集。您可以找到库here的当前草稿。

但是,如果您想了解pipes-parse如何使其工作,实现剩余部分的最简单方法是使用StateP来存储回送缓冲区。这只需要定义以下两个函数:

import Control.Proxy
import Control.Proxy.Trans.State

draw :: (Monad m, Proxy p) => StateP [a] p () a b' b m a
draw = do
    s <- get
    case s of
        []   -> request ()
        a:as -> do
            put as
            return a

unDraw :: (Monad m, Proxy p) => a -> StateP [a] p () a b' b m ()
unDraw a = do
    as <- get
    put (a:as)

draw首先查询回推缓冲区以查看是否存在任何存储的元素,如果可用则从堆栈中弹出一个元素。如果缓冲区为空,则它从上游请求新元素。当然,如果我们不能推回任何东西就没有任何关系,所以我们也定义unDraw将元素推入堆栈以便以后保存。

编辑:哎呀,我忘了提供剩余时间有用的有用示例。就像迈克尔所说,takeWhiledropWhile是残羹剩饭的有用案例。这是drawWhile函数(类似于Michael所称的takeWhile):

drawWhile :: (Monad m, Proxy p) => (a -> Bool) -> StateP [a] p () a b' b m [a]
drawWhile pred = go
  where
    go = do
        a <- draw
        if pred a
        then do
            as <- go
            return (a:as)
        else do
            unDraw a
            return []

现在假设您的制作人是:

producer () = do
    respond 1
    respond 3
    respond 4
    respond 6

...而且你把它与使用过的消费者联系起来:

consumer () = do
    evens <- drawWhile odd
    odds  <- drawWhile even

如果第一个drawWhile odd没有推回它绘制的最后一个元素,那么你会删除4,这将无法正确传递到第二个drawWhile even语句`。