向下游发信号通知上游耗尽

时间:2015-08-20 08:31:02

标签: haskell haskell-pipes

问题

使用Haskell pipes库,我尝试使用以下类型定义Pipe

signalExhausted :: Monad m => Pipe a (Value a) m r 

其中Value数据类型由:

定义
data Value a = Value a | Exhausted

管道应遵守以下法律:

toList (each [] >-> signalExhausted) ==                 [Exhausted]
toList (each xs >-> signalExhausted) == map Value xs ++ [Exhausted]

换句话说,管道应该等同于Pipes.Prelude.map Value,除了在处理完所有上游值后它应该产生额外的Exhausted,使下游有机会执行一些最终操作。 / p>

可以定义这样的Pipe吗?

实施例

> let xs = words "hubble bubble toil and trouble"
> toList $ each xs >-> signalExhausted
[Value "hubble", Value "bubble", Value "toil", Value "and", Value "trouble", Exhausted]

备注

我知道pipes-parse库提供了drawparseForever功能。这些看起来很有用,但我不太清楚如何将它们组合成符合上述规范的Pipe

1 个答案:

答案 0 :(得分:4)

无法定义像signalExhausted这样的管道,但是相当于(>-> signalExhausted)的功能可以。

>->pull category的专用版本。下游代理从上游代理中提取数据来驱动执行。下游代理向上游发送空请求()并阻塞,直到保持值的响应从上游代理返回。当上游代理用尽并且没有更多值要发回时,它return。您可以在each的定义中看到对这些示例很重要的return

each = F.foldr (\a p -> yield a >> p) (return ())
-- what to do when the data's exhausted ^                       

下游代理需要一个值才能继续运行,但管道库可能没有提供它的值,因此下游代理永远不会再运行。由于它永远不会再次运行,因此无法修改或响应数据。

这个问题有两个解决方案。最简单的方法是在上游管道上map Value,并在完成后添加yield Exhausted

import Pipes
import qualified Pipes.Prelude as P

data Value a = Value a | Exhausted
    deriving (Show)

signalExhausted p = p >-> P.map Value >> yield Exhausted

除了功能signalExhausted取代(>-> signalExhausted)之外,这正是您正在寻找的内容。

let xs = words "hubble bubble toil and trouble"
print . P.toList . signalExhausted $ each xs

[Value "hubble",Value "bubble",Value "toil",Value "and",Value "trouble",Exhausted]

这个问题的更一般的解决方案是停止上游代理返回,而是在耗尽时向下游发出信号。我在answer to a related question中演示了如何执行此操作。

import Control.Monad
import Pipes.Core

returnDownstream :: Monad m => Proxy a' a b' b m r -> Proxy a' a b' (Either r b) m r'
returnDownstream = (forever . respond . Left =<<) . (respond . Right <\\)

这会将每个respond替换为respond . Right,并将return替换为forever . respond . left,并将回复和响应一起发送到下游。

returnDownstream比您正在寻找的更为通用。我们可以演示如何使用它来重新创建signalExhaustedreturnDownstream转换回一个永不返回的管道,然后将其返回值作为Left的{​​{1}}值转发到下游。

Either

signalExhausted p = returnDownstream p >-> respondLeftOnce 是一个示例下游代理。下游代理可以识别respondLeftOnce中保留的常规值和Right中保存的返回值。

Left