考虑以下计划。它永远运行并没有任何用处,但ghci中的内存消耗是恒定的:
--NoExplode.hs
module Main (main) where
test :: [Int] -> IO()
test lst = do
print "test"
rList lst
rList :: [Int] -> IO ()
rList [] = return ()
rList (x:xs) = do
rList xs
main = do
test [1..]
现在考虑以上的以下简单修改版本。当这个程序在ghci中运行时,内存会爆炸。唯一的区别是print "test"
现已分配到x
do
块test
中的--Explode.hs
module Main (main) where
test :: [Int] -> IO()
test lst = do
x <- print "test"
rList lst
rList :: [Int] -> IO ()
rList [] = return ()
rList (x:xs) = do
rList xs
main = do
test [1..]
。
print "test"
为什么将x <- print "test"
更改为{{1}}会导致ghci爆炸?
P.S。我在试图理解Memory exploding upon writing a lazy bytestring to file in ghci时遇到过这种情况,而那里的问题(我认为)基本上是对上述问题进行了解释。感谢
答案 0 :(得分:10)
免责声明:我不是GHCi专家,也不是GHC核心的好人。既然我已经失去了信誉,那就让我们试着了解会发生什么:
GHCi retains all evaluated CAFs:
通常,在评估之间保留对已加载模块中的顶级表达式(也称为CAF或常规应用表单)的任何评估。
现在您可能想知道为什么两个版本之间存在如此大的差异。让我们看一下-ddump-simpl
的核心。请注意,您可能希望在自己转储程序时删除-dsuppress-all
。
❯ ghc SO.hs -ddump-simpl -fforce-recomp -O0 -dsuppress-all
[1 of 1] Compiling Main ( SO.hs, SO.o )
==================== Tidy Core ====================
Result size of Tidy Core = {terms: 29, types: 28, coercions: 0}
$dShow_rq2
$dShow_rq2 = $fShow[] $fShowChar
Rec {
rList_reI
rList_reI =
\ ds_dpU ->
case ds_dpU of _ {
[] -> return $fMonadIO ();
: x_aho xs_ahp -> rList_reI xs_ahp
}
end Rec }
main
main =
>>
$fMonadIO
(print $dShow_rq2 (unpackCString# "test"))
(rList_reI (enumFrom $fEnumInt (I# 1)))
main
main = runMainIO main
重要的部分是[1..]
的位置,几乎在最后:
enumFrom $fEnumInt (I# 1))
如您所见,该列表不是CAF。但是如果我们改为使用爆炸式版本会发生什么?
❯ ghc SO.hs -ddump-simpl -fforce-recomp -O0 -dsuppress-all
[1 of 1] Compiling Main ( SO.hs, SO.o )
==================== Tidy Core ====================
Result size of Tidy Core = {terms: 32, types: 31, coercions: 0}
$dShow_rq3
$dShow_rq3 = $fShow[] $fShowChar
Rec {
rList_reI
rList_reI =
\ ds_dpV ->
case ds_dpV of _ {
[] -> return $fMonadIO ();
: x_ahp xs_ahq -> rList_reI xs_ahq
}
end Rec }
lst_rq4
lst_rq4 = enumFrom $fEnumInt (I# 1)
main
main =
>>=
$fMonadIO
(print $dShow_rq3 (unpackCString# "test"))
(\ _ -> rList_reI lst_rq4)
main
main = runMainIO main
突然出现了一个新的顶级表达式,即lst_rq4
,它会生成列表。如前所述,GHCi保留了对顶级表达式的评估,因此lst_rq4
也将保留。
现在可以选择放弃评估:
启用
+r
会导致在每次评估后丢弃对顶级表达式的所有评估(在单次评估期间仍会保留这些评估)。
但是,因为&#34;他们仍然在一次评估期间保留了#34;在这种情况下,即使:set +r
也无法帮助您。不幸的是,我无法回答为什么GHC引入了一个新的顶级表达式。
该列表仍然是顶级表达式:
main2
main2 = eftInt 1 2147483647
有趣的是,GHC实际上并没有创建无限列表,因为Int
是有限的。
在这种情况下,如果将列表放在测试中,则可以删除它:
test = do
x <- print "test"
rList [1..]
这将阻止GHC创建顶级表达。
但是,我无法就此提出一般性建议。不幸的是,我的Haskell-fu还不够好。