6

问题

使用 Haskell管道库,我试图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在所有上游值都被处理后产生一个额外的,让下游有机会执行一些最终操作。

可以这样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符合上述规范的。

4

1 回答 1

4

signalExhausted无法定义类似管道,但等效于(>-> signalExhausted)can 的函数。

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

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

下游代理需要一个值才能继续运行,但管道库无法提供任何值,因此下游代理永远不会再次运行。由于它永远不会再次运行,因此它无法修改或对数据做出反应。

这个问题有两种解决方案。最简单的方法是越过上游管道并在完成后添加一个。map Valueyield 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]

这个问题的更通用的解决方案是阻止上游代理返回,而是在它耗尽时向下游发出信号。我在一个相关问题的回答中演示了如何做到这一点。

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 <\\)

这将替换 eachrespondrespond . Right替换returnforever . respond . left,将返回与响应一起发送到下游。

returnDownstream比你要找的更一般。我们可以演示如何使用它来重新创建signalExhausted. returnDownstream将返回的管道转换为永不返回的管道,而是将其返回值作为Leftan 的值向下游转发Either

signalExhausted p = returnDownstream p >-> respondLeftOnce

respondLeftOnce是一个示例下游代理。下游代理可以区分保存在中的常规值和保存在中Right的返回值Left

respondLeftOnce :: Monad m => Pipe (Either e a) (Value a) m ()
respondLeftOnce = go
    where
        go = do
            ea <- await
            case ea of
                Right a -> yield (Value a) >> go                    
                Left  _ -> yield Exhausted       -- The upstream proxy is exhausted; do something else  
于 2015-08-20T14:04:56.297 回答