如何编写Haskell Pipes"sum"函数?

Ana*_*Ana 8 haskell pipe

我正在尝试pipes通过编写自己的sum函数来学习包,我感到难过.我想不是从使用的效用函数Pipes.Prelude(因为它有sumfold等功能,这使得它微不足道),并仅作为说明使用该信息Pipes.Tutorial.本教程没有讨论构造函数Proxy,但如果我查看它的源代码sumfold使用那些构造函数,我想知道是否有可能在sum不知道这些低级细节的情况下编写我的函数.

只要有值可用,我就无法理解这个函数如何能够继续获取值,然后以某种方式将该值返回给用户.我想类型将是:

sum' :: Monad m => Consumer Int m Int
Run Code Online (Sandbox Code Playgroud)

在我看来这可以工作,因为这个函数可以消耗值,直到没有更多,然后返回最后的总和.我会像这样使用它:

mysum <- runEffect $ inputs >-> sum'
Run Code Online (Sandbox Code Playgroud)

但是,函数in Pipes.Prelude具有以下签名:

sum :: (Monad m, Num a) => Producer a m () -> m a
Run Code Online (Sandbox Code Playgroud)

所以我想这是我的第一个障碍.为什么sum函数Producer作为参数而不是>->用于连接?


仅供参考我在danidiaz回答后得到以下结论:

sum' = go 0
  where
    go n p = next p >>= \x -> case x of
      Left _        -> return n
      Right (_, p') -> go (n + 1) p'
Run Code Online (Sandbox Code Playgroud)

dan*_*iaz 5

Consumers实际上他们能做的事情非常有限.它们无法检测到输入结束(管道解析使用不同的技术),当管道的某些其他部分停止时(例如Producer上游),该部分必须为管道提供结果值.因此,将总和放在返回值Consumer中将不起作用.

一些替代方案是:

  • 实现一个直接处理Producer内部函数的函数,或者可能使用类似的辅助函数next.有这种类型的适配器可以Producer向"更聪明"的消费者提供数据,例如Fold来自foldl包的消费者.

  • 继续使用a Consumer,但不是将sum放在the的返回值中,而是Consumer使用a WriterT作为基数monad,使用Sum Intmonoid作为累加器.这样,即使Producer首先停止,您仍然可以运行编写器来到累加器.但是,此解决方案可能效率较低.

方法的示例代码WriterT:

import Data.Monoid
import Control.Monad
import Control.Monad.Trans.Writer
import Pipes

producer :: Monad m => Producer Int m ()
producer = mapM_ yield [1..10]

summator :: Monad n => Consumer Int (WriterT (Sum Int) n) ()
summator = forever $ await >>= lift . tell .  Sum

main :: IO ()
main = do
   Sum r <- execWriterT . runEffect $ producer >-> summator
   print r
Run Code Online (Sandbox Code Playgroud)