haskell – 懒惰版的mapM

假设在使用IO时,我收到了大量的项目:

as <- getLargeList

现在,我试图应用fn :: a – > IO b为:

as <- getLargeList
bs <- mapM fn as

mapM有类型mapM :: Monad m => (a→m b)→> [a] – > m [b],这就是我所需要的类型匹配.但它建立内存中的所有链,直到返回结果.我正在寻找类似的mapM,它将工作懒惰,所以我可以使用bs的头,而尾部仍在建设.

最佳答案
不要使用unsafeInterleaveIO或任何懒惰IO.这正是创建迭代以解决的问题:避免惰性IO,这导致不可预测的资源管理.诀窍是永远不要使用迭代来构建列表,并且不断地使用它,直到你完成使用它.我将使用我自己的图书馆,管道的例子来证明这一点.

首先,定义:

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黑客.要更改您需要的元素数量,只需更改您传递的值即可

有关更多示例,请阅读Control.Pipe.Tutorial上的pipes tutorial.

要了解更多关于为什么懒惰IO会导致问题的原因,请阅读Oleg关于该主题的原始幻灯片,您可以在其中找到here.他使用懒惰IO来解释问题.任何时候你都不得不使用懒惰的IO,你真正想要的是一个迭代库.

转载注明原文:haskell – 懒惰版的mapM - 代码日志