[英]How to write a Haskell Pipes “sum” function?
我試圖通過編寫自己的sum
函數來學習pipes
程序包,但我感到很困惑。 我不想使用Pipes.Prelude
的實用程序功能(因為它具有sum
和fold
以及其他使它變得微不足道的功能),而僅使用如Pipes.Tutorial
所述的信息。 本教程沒有討論Proxy
的構造函數,但是如果我查看sum
和fold
的來源,它將使用這些構造函數,並且我想知道是否可以在不了解這些底層細節的情況下編寫sum
函數。
只要有可用的值,我就很難確定該函數將如何繼續獲取值,然后以某種方式將總和返回給用戶。 我猜類型是:
sum' :: Monad m => Consumer Int m Int
在我看來這可行,因為此函數可能會消耗值,直到沒有更多值為止,然后返回最終的總和。 我會這樣使用它:
mysum <- runEffect $ inputs >-> sum'
但是, Pipes.Prelude
的函數具有以下簽名:
sum :: (Monad m, Num a) => Producer a m () -> m a
所以我想這是我的第一個障礙。 為什么sum
函數將Producer
作為參數而不是使用>->
進行連接?
僅供參考,在達尼迪亞茲回答后,我得出以下結論:
sum' = go 0
where
go n p = next p >>= \x -> case x of
Left _ -> return n
Right (_, p') -> go (n + 1) p'
Consumers
實際上只能做些有限的事情。 他們無法檢測到輸入結束( pipes-parse為此使用了不同的技術),並且當流水線的其他部分停止時(例如上游的Producer
) ,該部分就是必須提供輸入結果的部分。管道。 因此,將總和作為Consumer
的返回值通常不會起作用。
一些替代方法是:
實現直接處理Producer
內部的功能,或者使用諸如next
的輔助功能。 有這種類型的適配器可以將Producer
數據提供給“更智能”的消費者,例如foldl包中的Fold
。
繼續使用Consumer
,但不要將總和作為Consumer
的返回值,而應使用WriterT
作為基本monad,並將Sum Int
monoid作為累加器。 這樣,即使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
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.