Как уже отмечали другие, 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
.
За Хаскелем
Хотя вывод по региону полезен в некоторых случаях, как обсуждалось выше, представляется, что трудно эффективно примирить с ленивой оценкой (см. и 1010 * * * * * комментариев Саймона Пейтона Джонса Эдварда Кметта). Например, рассмотрим
f :: Integer -> Integer
f n = product [1..n]
Кто-то может испытать желание разместить список [1..n]
в стеке и освободить его после возврата f
, но это будет катастрофическим: это изменит f
при использовании памяти O (1) (при сборке мусора) в O (n) памяти.
В 1990-х и начале 2000-х годов была проделана большая работа по выводу региона для функционального языка строгого ML. Мадс Тофт, Ларс Биркедал, Мартин Эльсман, Нильс Халленберг написали вполне читабельную ретроспективу о своей работе по определению региона, большую часть которой они интегрировали в MLKit-компилятор . Они экспериментировали с управлением памятью исключительно на основе регионов (т.е. без сборщика мусора), а также с гибридным управлением памятью на основе регионов / мусора, и сообщили, что их тестовые программы работали «в 10 раз быстрее и в 4 раза медленнее», чем чистый мусор. собранные версии.