使用管道库,我想编写一个程序来从某个源读取数据并将其累积(例如,使用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
当然,这个程序甚至没有正确执行简化的任务,因为它累积到 1 而不是 6。如果我没记错的话,这种行为可以通过管道在终止之前只读取一个元素这一事实来解释。重复直到输入结束,我想出了以下内容,
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
?
鉴于 for 的许多用例pipes
是处理大型数据集的长时间运行的流程,Control.Proxy.Prelude
围绕严格WriterP
而不是WriterT
. 目前感觉中的代理转换pipes
器是二等公民,存在但缺少许多WriterT
如此方便的组合器。