用管道的WriterP编写一个简单的累加器

bga*_*ari 4 haskell haskell-pipes

使用管道库,我想编写一个程序来从某个源读取数据并单独累积它(比如说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
Run Code Online (Sandbox Code Playgroud)

当然,虽然这适用于小型源,但由于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
Run Code Online (Sandbox Code Playgroud)

当然,这个程序甚至没有正确地执行简化的任务,因为它累积到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'
Run Code Online (Sandbox Code Playgroud)

但是,这段代码返回的累加器为0.这是为什么?有没有像我fold提供的功能pipes

鉴于许多用例pipes都是使用大型数据集的长时间运行的进程,Control.Proxy.Prelude围绕严格WriterP而不是WriterT?构建折叠是否有意义?目前感觉代理变压器pipes是二等公民,现在但缺乏许多组合使用的WriterT方便.

Gab*_*lez 7

我正在添加一个新答案,因为我已经解决了这个问题pipes-3.3,我刚刚上传到Hackage.管道背后的理论表明,您期望的全局行为一直是正确的行为,WriterP现在表现为全局,因此您可以在管道中折叠.

我修改了你的例子,表明你将使用pipes-3.3以下方法实现它:

import Control.Proxy
import Control.Proxy.Trans.Writer

main = do
    let source = enumFromToS (0::Int) 5
    a <- runProxy $ execWriterK $ source >-> sumD
    print a
Run Code Online (Sandbox Code Playgroud)

您现在还可以检索管道中折叠的结果.例如,这是完全有效的:

chunksOf :: (Monad m, Proxy p) => Int -> () -> Pipe p a [a] m r
chunksOf n () = runIdentityP $ forever $ do
    -- The unitU discards the values that 'toListD' reforwards
    as <- execWriterK (takeB_ n >-> toListD >-> unitU) ()
    respond as
Run Code Online (Sandbox Code Playgroud)

这是一个示例用法:

>>> runProxy $ getLineS >-> takeWhileD (/= "quit") >-> chunksOf 3 >-> printD
1<Enter>
2<Enter>
3<Enter>
["1","2","3"]
4<Enter>
5<Enter>
6<Enter>
["4","5","6"]
quit
Run Code Online (Sandbox Code Playgroud)

很抱歉第一次得错答案!