В языках программирования замыкания являются популярной и часто желаемой функцией. Википедия говорит (выделение мое):
В информатике замыкание (...) - это функция вместе со средой ссылки для нелокальных переменных этой функции. Закрытие позволяет функции обращаться к переменным вне ее непосредственной лексической области.
Таким образом, замыкание - это, по сути, (анонимное?) Значение функции, которое может использовать переменные вне своей области видимости. По моему опыту, это означает, что он может получить доступ к переменным, которые находятся в области действия в точке его определения.
На практике эта концепция, похоже, расходится, по крайней мере, за пределами функционального программирования. Разные языки реализуют разную семантику, даже кажется, что существуют войны мнений. Многие программисты, похоже, не знают, что такое замыкания, рассматривая их как нечто большее, чем анонимные функции.
Кроме того, кажется, существуют серьезные препятствия при реализации замыканий. Наиболее примечательно, что Java 7 должен был включать их, но эта функция была перенесена в будущий выпуск.
Почему замыкания так трудно (понять и понять)? Это слишком широкий и расплывчатый вопрос, поэтому позвольте мне сосредоточиться на следующих взаимосвязанных вопросах:
- Есть ли проблемы с выражением замыканий в общих семантических формализмах (маленький шаг, большой шаг, ...)?
- Разве существующие системы типов не подходят для затворов и не могут быть легко расширены?
- Проблематично ли привести замыкания в соответствие с традиционным, основанным на стеке переводом процедур?
Обратите внимание, что вопрос в основном относится к процедурным, объектно-ориентированным языкам и языкам сценариев в целом. Насколько я знаю, функциональные языки не имеют никаких проблем.
источник
Ответы:
Могу ли я направить вас на страницу Википедии о проблеме Фунарга ? По крайней мере, так люди из компилятора ссылались на проблему реализации замыкания.
Хотя это определение имеет смысл, оно не помогает описать проблему реализации первоклассных функций на традиционном языке, основанном на стеке времени выполнения. Когда дело доходит до вопросов реализации, функции первого класса можно условно разделить на два класса:
Первый случай (нисходящие funargs) не так сложен в реализации и может быть найден даже на более старых процедурных языках, таких как Algol, C и Pascal. Тип C обходит проблему, поскольку он не позволяет вложенные функции, но Algol и Pascal ведут необходимую бухгалтерию, чтобы внутренние функции могли ссылаться на переменные стека внешней функции.
Во втором случае (вверху funargs), с другой стороны, требуется, чтобы записи активации были сохранены вне стека, в куче. Это означает, что утечка ресурсов памяти очень проста, если языковая среда выполнения не включает сборщик мусора. В то время как почти все мусор собрано сегодня, требование одного все еще является значительным дизайнерским решением и было даже более того некоторое время назад.
Что касается конкретного примера Java, то, если я правильно помню, основная проблема заключалась не в возможности реализовать замыкания, а в том, как представить их в языке таким образом, чтобы он не был избыточен с существующими функциями (такими как анонимные внутренние классы) и это не противоречило существующим функциям (например, проверенные исключения - проблема, которую нетрудно решить и о которой большинство людей сначала не задумываются).
Я также могу думать о других вещах, которые делают функции первого класса менее тривиальными для реализации, такие как решение, что делать с «магическими» переменными, такими как this , self или super, и как взаимодействовать с существующими операторами потока управления, такими как break и return (мы хотим разрешить нелокальные возвраты или нет?). Но, в конце концов, недавняя популярность первоклассных функций, кажется, указывает на то, что языки, в которых их нет, в основном делают это по историческим причинам или из-за какого-то значительного конструктивного решения на ранних этапах.
источник
ref
параметр»). Если вызывающая сторона инкапсулирует все переменные, представляющие интерес в структуре, делегат может быть полностью статичным, избегая необходимости выделения кучи. Компиляторы не предлагают никакой приятной синтаксической справки для таких конструкций, но Framework может их поддерживать.Мы можем посмотреть, как замыкания реализованы в C #. Масштаб преобразований, выполняемых компилятором C #, дает понять, что их способ реализации замыканий - довольно большая работа. Могут быть более простые способы реализации замыканий, но я думаю, что команда компилятора C # будет знать об этом.
Рассмотрим следующее псевдо-C # (я вырезал немного C #-специфичных вещей):
Компилятор превращает это во что-то вроде этого:
(на самом деле, переменная f все равно будет создана, где f - это «делегат» (= указатель на функцию), но этот делегат по-прежнему связан с объектом theClosureObject - я оставил эту часть для ясности для тех, кто не знаком с C #)
Это преобразование является довольно масштабным и сложным: рассмотрим замыкания внутри замыканий и взаимодействие замыканий с остальными возможностями языка C #. Я могу себе представить, что эта функция была отодвинута назад для Java, поскольку в Java 7 уже появилось довольно много новых функций.
источник
Чтобы ответить на часть вашего вопроса. Формализм, описанный Моррисеттом и Харпером, охватывает семантику больших и малых шагов полиморфных языков высшего порядка, содержащих замыкания. До этого есть документы, в которых указана та семантика, которую вы ищете. Посмотрите, например, на машину SECD . Добавить изменяемые ссылки или изменяемые локальные объекты в эту семантику просто. Я не вижу каких-либо технических проблем в предоставлении такой семантики.
источник