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

A pipe like signalExhausted can't be defined, but a function equivalent to (>-> signalExhausted) can.

>->pullカテゴリの特殊バージョンです。実行は、上流のプロキシからデータをプルする下流のプロキシによって駆動されます。ダウンストリーム プロキシは空のリクエストを()アップストリームに送信し、値を保持するレスポンスがアップストリーム プロキシから返されるまでブロックします。アップストリーム プロキシが使い果たされ、送り返す値がなくなると、returns. returnの定義で、これらの例にとって重要なことがわかりますeach

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

ダウンストリーム プロキシは実行を継続するために値を必要としますが、パイプ ライブラリが提供できる可能性のある値がないため、ダウンストリーム プロキシは二度と実行されません。二度と実行されないため、データを変更したり、データに反応したりすることはできません。

この問題には 2 つの解決策があります。最も簡単な方法は、上流のパイプに渡して、完了後に a を追加することです。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および に置き換え、応答returnとともにforever . respond . leftリターンを下流に送信します。

returnDownstream探しているものよりも一般的です。それを使用して を再作成する方法を示すことができますsignalExhaustedreturnDownstream戻るパイプを決して戻らないパイプに変換し、代わりにその戻り値Leftを an の値として下流に転送し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 に答える