如何折叠惰性类型列表([IO a])?

Yro*_*irg 5 performance haskell

我不知道它是否是一个有效的术语'懒惰类型'.但是,IO仍然很懒惰

import Control.Monad
import Data.List

result :: IO Double
result = foldl1' (liftM2 (+)) $ map return [1..10000000]

result' :: IO Double
result' = return $ foldl1' (+) [1..10000000]
Run Code Online (Sandbox Code Playgroud)

result很慢,并且使用大量内存,不像result'.怎么折叠[IO a]

Dan*_*her 7

resultIO Double不评估任何中间结果的情况下构造一个大值,仅在需要总结果时才发生,例如用于打印.foldl'将中间结果评估为弱头正常形式,即最外层构造函数或lambda.由于(在GHC中)IO a具有构造函数IO,因此折叠的中间结果具有形式

IO (some computation combined with another)
Run Code Online (Sandbox Code Playgroud)

并且IO每一步下的表达都变得更加复杂.

为避免这种情况,您不仅要强制中间IO值,还要强制它们返回的值,

main :: IO ()
main = foldlM' (\a -> fmap (a+)) 0 (map return [1.0 .. 10000000]) >>= print

foldlM' :: Monad m => (a -> b -> m a) -> a -> [b] -> m a
foldlM' foo a [] = return a
foldlM' foo a (b:bs) = do
    c <- foo a b
    c `seq` foldlM' foo c bs
Run Code Online (Sandbox Code Playgroud)

适合你的榜样.

  • 啊,好的,所以你把它绑定在文件中.所以`main`仍然在范围内,并且它是一个CAF,因此它所占用的内存无法释放.因为它是`IO`,所以`main`不是最终结果`return number`,而是产生它的整个计算(对于所有编译器都知道,它可能有副作用,应该重复).因此,这是一个巨大的"IO thunk". (2认同)