不要使用unsafeInterleaveIO
或懒惰IO为这一问题。这正是迭代创建时要解决的问题:避免惰性IO,这会导致不可预知的资源管理。诀窍是永远不会构建列表,并不断使用iteratees对其进行流式传输,直到完成使用。我将使用我自己的库中的示例pipes
来演示这一点。
首先,定义:
import Control.Monad
import Control.Monad.Trans
import Control.Pipe
-- Demand only 'n' elements
take' :: (Monad m) => Int -> Pipe a a m()
take' n = replicateM_ n $ do
a <- await
yield a
-- Print all incoming elements
printer :: (Show a) => Consumer a IO r
printer = forever $ do
a <- await
lift $ print a
现在我们是平均到我们的用户,要求他们生产对我们来说真是大名单:
prompt100 :: Producer Int IO()
prompt100 = replicateM_ 1000 $ do
lift $ putStrLn "Enter an integer: "
n <- lift readLn
yield n
现在,让我们来运行它:
>>> runPipe $ printer <+< take' 1 <+< prompt100
Enter an integer:
3<Enter>
3
它只提示用户输入一个整数,因为我们只需要一个整数!
如果你想从getLargeList
输出更换prompt100
,你只写:
yourProducer :: Producer b IO()
yourProducer = do
xs <- lift getLargeList
mapM_ yield xs
...然后运行:
>>> runPipe $ printer <+< take' 1 <+< yourProducer
这会懒洋洋地流列表中,从来没有建列表在内存中,全部不使用不安全的IO
黑客。要更改您需要的元素数量,只需更改您传递的值take'
有关更多示例,请参阅pipes
tutorial,地址为Control.Pipe.Tutorial
。
要了解为何懒IO引起的问题,阅读关于这个问题,你可以找到here奥列格的原始幻灯片。他擅长解释使用惰性IO的问题。任何时候你感觉不得不使用惰性IO,你真正想要的是一个迭代库。
也许这会有所帮助。 http://stackoverflow.com/questions/3270255/is-haskells-mapm-not-lazy –
安东,我读过这个话题,但我没有找到答案:是否有一个替代mapM的懒计算。 –
@DmitryBespalov不具有相同的类型签名。 'Monad'没有一个抽象的延迟效果,直到后来 - 这就是你必须做的,以使'mapM'变得更加懒散。 – Carl