Ответ 1
GHC реализует Haskell как машину для уменьшения графа. Представьте свою программу в виде графика с каждым значением как node, а строки - от каждого значения, от которого зависит значение. Кроме того, мы ленивы, поэтому вы действительно начинаете только с одного node - и чтобы оценить, что node, GHC должен "enter" его и открыть его до функции с аргументами. Затем он заменяет вызов функции телом функции и пытается уменьшить ее достаточно, чтобы получить ее в обычную форму головы и т.д.
Вышеприведенное является очень привлекательным, и я уверен, что вы получите некоторые необходимые детали в интересах краткости.
В любом случае, когда GHC вводит значение, он обычно заменяет его черной дырой, пока оценивается node (или, в зависимости от вашей терминологии, при закрытии закрытия). Это имеет ряд целей, Во-первых, он подключает потенциальную утечку пространства. Если node ссылается на значение, которое больше нигде не используется, черная дыра позволяет собирать мусор даже при оценке node. Во-вторых, это предотвращает определенные типы дубликатов, поскольку в многопоточной среде два потока могут пытаться ввести одно и то же значение. Черная дыра заставит второй поток блокировать, а не оценивать уже оцениваемое значение. Наконец, это случается, чтобы разрешить ограниченную форму обнаружения цикла, поскольку, если поток пытается повторно войти в свою собственную черную дыру, мы можем исключить исключение.
Вот немного более метафорическое объяснение. Если у меня есть ряд инструкций, которые перемещают черепаху (в логотипе) вокруг экрана, нет ни одного способа сказать, какую форму они будут производить, или эта форма заканчивается без их запуска. Но если, выполняя их, я замечаю, что путь черепахи пересек сам, я могу указать пользователю: "Ага! Черепаха пересекла свой путь!" Поэтому я знаю, что черепаха достигла места, которое было раньше - если путь - это схема, проходящая через узлы графа, то это говорит нам, что мы находимся в цикле. Тем не менее, черепаха также может войти, например, в расширяющуюся спираль. И он никогда не завершится, но он также никогда не пересечет свой предыдущий путь.
Таким образом, из-за использования черных дыр по нескольким причинам у нас есть некоторое понятие о отмеченном "пути", за которым последовала оценка. И если путь пересекает себя, мы можем сказать и выбросить исключение. Однако есть миллионы способов расхождения вещей, которые не связаны с самим пересечением путей. И в этих случаях мы не можем сказать и не вызывать исключения.
Для сверхъестественных технических подробностей о текущей реализации черных дыр см. доклад Саймона Марло из недавнего семинара исполнителей Haskell, "Планирование ленивой оценки на многоядерном" внизу http://haskell.org/haskellwiki/HaskellImplementorsWorkshop/2010.