Этот код суммирует содержимое двух (взаимно отмены) бесконечных списков, указанных на верхнем уровне:Почему этот код Haskell не исчерпывает кучу?
{-# language BangPatterns #-}
module Main where
unending1 :: [Int]
unending1 = cycle [1]
unending2 :: [Int]
unending2 = cycle [negate 1]
main :: IO()
main = do
let summator :: Int -> [Int] -> [Int] -> Int
summator !acc (i1 : rest1) (i2 : rest2) =
if acc > 100
then acc -- never happens
else summator (acc+i1+i2) rest1 rest2
print (summator 0 unending1 unending2)
компилировать этот код без оптимизации и низкий размер кучи, как это:
ghc -O0 -with-rtsopts="-M10m" Main.hs
Моя интуиция заключается в том, что этот код вызовет утечку памяти, потому что функция суммирования попытается «материализовать» два списка, а главы обоих из них находятся на верхнем уровне, поэтому они не будут отброшены.
Однако, когда я запускаю программу, она работает бессрочно без проблем.
Где я ошибаюсь?
Редактировать. Проверяя сгенерированное ядро с использованием -ddump-simpl
, списки, похоже, остаются на верхнем уровне. Например:
Result size of Tidy Core = {terms: 77, types: 52, coercions: 0}
-- RHS size: {terms: 5, types: 3, coercions: 0}
unending1 :: [Int]
[GblId, Str=DmdType]
unending1 =
cycle
@ Int (GHC.Types.: @ Int (GHC.Types.I# 1#) (GHC.Types.[] @ Int))
Проверили ли вы сердечник? Если бы я должен был догадаться, я бы сказал, что 'unendingN' перемещаются в локальную переменную или встроенную, а затем они могут быть собраны в мусор. (Даже если в качестве CAF верхнего уровня можно ожидать, что они останутся в памяти). – chi
@chi Я не очень разбираюсь в чтении ядра, но я сгенерировал его с помощью '-ddump-simpl', и списки, похоже, остаются на верхнем уровне. – danidiaz
Теперь я проверил Core, но это не так. Вызов из основного списка в списки не вложен или не сделан локальным. Вероятно, глобальные CAF теперь GC'd, как указывает András Kovács ниже. – chi