2012-09-26 40 views
11

想,我得到的项目大名单与IO工作时:懒惰版本MAPM的

as <- getLargeList 

现在,我想申请fn :: a -> IO bas

as <- getLargeList 
bs <- mapM fn as 

mapM有键入mapM :: Monad m => (a -> m b) -> [a] -> m [b],这就是我需要的类型匹配。但它会建立内存中的所有链,直到返回结果。我正在寻找模拟mapM,这将延迟工作,以便我可以使用bs头,而尾巴仍在建设。

+1

也许这会有所帮助。 http://stackoverflow.com/questions/3270255/is-haskells-mapm-not-lazy –

+0

安东,我读过这个话题,但我没有找到答案:是否有一个替代mapM的懒计算。 –

+0

@DmitryBespalov不具有相同的类型签名。 'Monad'没有一个抽象的延迟效果,直到后来 - 这就是你必须做的,以使'mapM'变得更加懒散。 – Carl

回答

18

不要使用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,你真正想要的是一个迭代库。

+5

+100为管库 –

+2

Gabriel,谢谢你的回答!这是非常有用的图书馆,谢谢! –

+0

@DmitryBespalov不客气!我总是乐于提供帮助。 –

6

的IO单子是否有一个机构推迟的影响。它被称为unsafeInterleaveIO。你可以用它得到想要的效果:

import System.IO.Unsafe 

lazyMapM :: (a -> IO b) -> [a] -> IO [b] 
lazyMapM f [] = return [] 
lazyMapM f (x:xs) = do y <- f x 
         ys <- unsafeInterleaveIO $ lazyMapM f xs 
         return (y:ys) 

这就是懒惰IO如何实现的。实际执行效果的顺序很难预测,并且将根据评估结果列表元素的顺序来确定是不安全的。由于这个原因,f中的任何IO效应都是良性的,因为它们应该是对秩序不敏感的。一个通常效果良好的好例子是从只读文件中读取。

+1

从我的角度来看,在Haskell中使用System.IO.Unsafe就像是黑客攻击。由于其“不安全”行为,我宁愿避免使用它。不过,我感谢你的回答。 –