因此,懒惰的论点是它可以更好地分离关注点。如果我知道如何提供数据,我不应该假设该数据的用户需要一些有限的数据块。懒惰允许我产生一个无限的列表,然后将if的处理/切割推迟给消费者。
我想将这个想法扩展到包括monadic效应。这是我的代码
module Main where
import Control.Monad.Writer
longComputation :: Writer [String] [Int]
longComputation = do
let list = iterate (+1) 0
mapM (\n -> tell [show n] >> return n) list
twoSmallComputations :: Writer [String] [Int]
twoSmallComputations = do
first <- fmap (take 10) longComputation
second <- fmap (take 10) longComputation
return $ first ++ second
main :: IO ()
main = do
let (res, log') = runWriter twoSmallComputations
mapM_ print res
你可以看到记录是&#34;耦合&#34;随着结果的产生。在main
函数中,我打印两个较小计算的结果,每个计算只取一个无限计算的一部分。这按预期工作,我得到两组从0到9的数字作为输出。
现在,如果我将最后一行更改为mapM_ print log'
,我将无法获得相同的输出。相反,我得到了无限的数字列表。
现在,我理解为什么这样做会如此,所以我正在寻找一种不同的方法。
有没有办法将效果与实际生成的数据联系起来,以便它也被削减?因为目前的情况我必须从... -> m [a]
函数回退到... -> Int -> m [a]
个函数。
答案 0 :(得分:3)
如果您想要的流效果仅在流要求的范围内执行,那么您最好使用流媒体库,例如pipes
或conduit
。例如,pipes
中的代码可能如下所示:
import Control.Monad.Writer
import Pipes
import qualified Pipes.Prelude as P
ints :: Monad m => Producer Int m r
ints = go 0 where go n = yield n >> go (n + 1)
longComputation :: MonadWriter [String] m => Producer Int m r
longComputation = for ints $ \n -> tell [show n] >> yield n
twoSmallComputations :: MonadWriter [String] m => Producer Int m ()
twoSmallComputations = do
longComputation >-> P.take 10
longComputation >-> P.take 10
main = do
let (res, log') = runWriter $ P.toListM twoSmallComputations
mapM_ print log'
此处,ints
是一个产生Int
- s的无限流。 longComputation
遍历ints
的输出并添加Writer
效果。 twoSmallComputations
是一个复合流,首先运行longComputation >-> P.take 10
到耗尽,然后再次运行它。
mapM_ print log'
现在只打印预期的20个数字。
请参阅优秀的pipes tutorial以获取进一步的参考。