パイプライブラリを使用して、あるソースからデータを読み取り、それをモノイド的に蓄積するプログラムを作成したいと思います(たとえば、を使用してSum
)。これを行う最も簡単な方法は、
import Control.Proxy as
import Data.Monoid (Sum)
main = do
let source = enumFromToS (0::Int) 5
a <- runWriterT $ runProxy $ source >-> foldD Sum
print a
もちろん、これは小さなソースでは機能しますが、大きな入力は、WriterT
アキュムレータの怠惰な性質のために恐ろしいスタックオーバーフローを引き起こします。
ありがたいことに、これはpipes
これを予期しているようで、WriterP
プロキシに厳密なアキュムレータを提供します。残念ながら、このプロキシを取り巻くドキュメントはかなりまばらです。少し突っ込んだ後(そして問題を単純化して、代わりにダウンストリーム要素ごとに1を累積する)、私はこのプログラムに行きました、
import Control.Proxy
import Control.Proxy.Trans.Writer
import Data.Monoid (Sum)
main = do
let source = enumFromToS (0::Int) 5
a <- runProxy $ runWriterK $ source >-> \x->tell (Sum 1::Sum Int)
print a
もちろん、このプログラムは、6ではなく1に累積されるため、簡略化されたタスクを正しく実行しません。私が間違っていない場合、この動作は、パイプが終了する前に1つの要素のみを読み取るという事実によって説明されます。入力が終わるまで繰り返すために、私は次のことを思いついた、
import Control.Proxy
import Control.Proxy.Trans.Writer
import Data.Monoid (Sum)
main = do
let source = enumFromToS (0::Int) 5
a <- runProxy $ runWriterK $ source >-> fold Sum
print a
fold :: (Monad m, Proxy p, Monoid w) => (a -> w) -> a' -> WriterP w p a' a a' a m r
fold f = go
where go x = do a <- request x
tell $ f a
x' <- respond a
go x'
ただし、このコードはアキュムレータ0を返します。これはなぜですか?私のような機能はありfold
ますpipes
か?
の多くのユースケースpipes
が大規模なデータセットを処理する長時間実行プロセスであることを考えると、フォールドインを厳密ではなくControl.Proxy.Prelude
厳密に構築することは意味がありませんか?現在、のプロキシトランスフォーマーは二流市民であり、存在しているように感じますが、非常に便利なコンビネータの多くが不足しています。WriterP
WriterT
pipes
WriterT