在monadic计算中保持惰性模块性

时间:2015-07-23 08:33:35

标签: haskell

因此,懒惰的论点是它可以更好地分离关注点。如果我知道如何提供数据,我不应该假设该数据的用户需要一些有限的数据块。懒惰允许我产生一个无限的列表,然后将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]个函数。

1 个答案:

答案 0 :(得分:3)

如果您想要的流效果仅在流要求的范围内执行,那么您最好使用流媒体库,例如pipesconduit。例如,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以获取进一步的参考。