Ответ 1
Отказ от ответственности. Я не эксперт GHCi, а также не очень хорошо разбираюсь в ядре GHC. Теперь, когда я потерял доверие, попробуем понять, что происходит:
GHCi и CAFs
GHCi сохраняет все оцениваемые CAF:
Обычно любая оценка выражений верхнего уровня (иначе называемых 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
заставляет каждую оценку высших выражений отбрасываться после каждой оценки (они все еще сохраняются во время одной оценки).
Но поскольку "они все еще сохраняются во время одной оценки", даже :set +r
вам не поможет. К сожалению, я не могу ответить, почему GHC вводит новое выражение верхнего уровня.
Почему это происходит даже в оптимизированном коде?
Список все еще является выражением верхнего уровня:
main2
main2 = eftInt 1 2147483647
Забавно, что GHC на самом деле не создает бесконечный список, так как Int
ограничен.
Как можно избавиться от утечки?
В этом случае вы можете избавиться от него, если вы разместите список в тесте:
test = do
x <- print "test"
rList [1..]
Это предотвратит создание GHC выражения верхнего уровня.
Однако я не могу дать общий совет по этому поводу. К сожалению, мой Haskell-fu еще недостаточно хорош.