Как и другие уже указывал, Haskell требует автоматического , динамического управления памятью: автоматическое управление памятью необходимо потому , что ручное управление памятью небезопасно; динамическое управление памятью необходимо, поскольку для некоторых программ время жизни объекта можно определить только во время выполнения.
Например, рассмотрим следующую программу:
main = loop (Just [1..1000]) where
loop :: Maybe [Int] -> IO ()
loop obj = do
print obj
resp <- getLine
if resp == "clear"
then loop Nothing
else loop obj
В этой программе список [1..1000]
должен храниться в памяти, пока пользователь не наберет «очистить»; поэтому время жизни этого должно определяться динамически, и поэтому необходимо динамическое управление памятью.
Таким образом, в этом смысле необходимо автоматическое выделение динамической памяти, и на практике это означает: да , Haskell требует сборщика мусора, поскольку сборка мусора является наиболее производительным автоматическим диспетчером динамической памяти.
Тем не мение...
Хотя сборщик мусора необходим, мы могли бы попытаться найти некоторые особые случаи, когда компилятор может использовать более дешевую схему управления памятью, чем сборка мусора. Например, учитывая
f :: Integer -> Integer
f x = let x2 = x*x in x2*x2
мы можем надеяться, что компилятор обнаружит, что x2
может быть безопасно освобождено при f
возврате (вместо ожидания освобождения сборщика мусора x2
). По сути, мы просим компилятор выполнить escape-анализ, чтобы преобразовать выделение памяти в кучу со сборкой мусора в выделения в стеке, где это возможно.
Это не так уж и неразумно: компилятор jhc haskell делает это, а GHC - нет. Саймон Марлоу говорит что сборщик мусора GHC по поколениям делает анализ побега практически ненужным.
jhc на самом деле использует изощренную форму анализа выхода, известную как определение региона . Рассматривать
f :: Integer -> (Integer, Integer)
f x = let x2 = x * x in (x2, x2+1)
g :: Integer -> Integer
g x = case f x of (y, z) -> y + z
В этом случае упрощенный анализ выхода сделает вывод, что он x2
ускользает из f
(потому что он возвращается в кортеже) и, следовательно, x2
должен быть размещен в куче со сборкой мусора. С другой стороны, определение региона может обнаружить, что x2
может быть освобождено при g
возврате; идея здесь в том, что x2
следует размещать в g
регионе России, а неf
регионе России.
За пределами Haskell
Хотя в некоторых случаях, как обсуждалось выше, полезен региональный вывод, кажется, что его трудно эффективно согласовать с ленивым вычислением (см. Комментарии Эдварда Кметта и Саймона Пейтона Джонса ). Например, рассмотрим
f :: Integer -> Integer
f n = product [1..n]
Может возникнуть соблазн выделить список [1..n]
в стеке и освободить его после f
возврата, но это было бы катастрофой: это изменило быf
с использования памяти O (1) (при сборке мусора) на память O (n).
В 1990-х и начале 2000-х годов была проделана обширная работа по выводу регионов для строгого функционального языка ML. Мадс Тофте, Ларс Биркедал, Мартин Эльсман, Нильс Халленберг написали довольно читаемую ретроспективу своей работы над логическим выводом регионов, большую часть которой они интегрировали в компилятор MLKit . Они экспериментировали с чисто региональным управлением памятью (т.е. без сборщика мусора), а также с гибридным управлением памятью на основе региона / сборщика мусора, и сообщили, что их тестовые программы выполнялись «в 10 раз быстрее и в 4 раза медленнее», чем чистый мусор. собрал версии.