Другие ответы объясняют, что вы не можете делать то, что вы ищете: вот некоторые практические причины почему вы не можете.
Рассмотрим фрагмент кода, подобный следующему:
(let ((a 1) (b 3) (c 2))
(lambda (x)
(+ (* a x x) (* b x) c)))
Давайте представим, что это компилируется: что будет делать разумный компилятор? Ну, вполне очевидно, что это может превратить это в следующее:
(lambda (x)
(+ (* 1 x x) (* 3 x) 2)
(а затем, вероятно, в
(lambda (x)
(+ (* x x) (* 3 x) 2))
и, возможно, дальше в
(lambda (x)
(+ (* x (+ x 3)) 2))
), прежде чем, наконец,
Ни одно из этих преобразований тела функции не ссылается ни на одну из лексических привязок, введенных замыканием. Вся среда была скомпилирована.
Так что еслиТеперь я хотел заменить эту функцию в этой среде, но на другую, ну, среды больше нет: я не могу.
Ну, вы могли бы поспорить, что это слишком простой случай, так как функцияне мутирует ни одну из своих закрытых переменных. Хорошо, рассмотрим что-то вроде этого:
(defun make-box (contents)
(values
(lambda ()
contents)
(lambda (new)
(setf contents new))))
make-box
возвращает две функции: читатель и писатель, которые закрывают содержимое поля. И это общее состояние не может быть полностью скомпилировано.
Но нет абсолютно никакой причины, почему закрытое состояние, например, все еще знает, что закрываемая переменная была вызвана contents
после make-box
было скомпилировано (или даже раньше). Например, обе функции могут ссылаться на некоторый вектор лексического состояния, и обе знают, что в источнике contents
есть первый элемент этого вектора. Все имена пропали, поэтому было бы невозможно заменить одну из функций, разделяющих это состояние, на другую.
Кроме того, в реализациях с различными компиляторами и интерпретаторами вообще нет причин, почему интерпретатордолжен иметь общее представление закрытого лексического состояния с компилятором (и компилятор, и интерпретатор могут иметь несколько разных представлений). И на самом деле спецификация CL решает эту проблему - запись для compile говорит:
Последствия не определены, если лексическая среда, окружающая компилируемую функцию, содержит какие-либо привязки, кромедля макросов, символьных макросов или объявлений.
и эта оговорка предназначена для того, чтобы иметь дело со случаями, подобными этому: если у вас есть набор функций, которые разделяют некоторое лексическое состояние, то, если компилятор имеетотличается представление лексического состояния от интерпретатора, составление только одного из них проблематично. (Я обнаружил это, пытаясь сделать что-то ужасное с общим лексическим состоянием на D-машине примерно в 1989 году, и какой-то добрый член комитета объяснил мне мою путаницу.)
Приведенные выше примерыЯ должен убедить вас, что замена функций, которые разделяют лексическое состояние, на другие функции невозможна простым способом. Но, ну, «невозможно простым способом» - это не то же самое, что «невозможно». Например, спецификация языка могла бы просто сказать, что это должно быть возможно, и требовать, чтобы реализации как-то заставили его работать:
- для первого примера, реализация может либо решить не компилировать среду таким образом,снижая производительность или, возможно, выбрав его компиляцию, но сохраняя записи о том, что это было, чтобы ее можно было восстановить вокруг какой-то новой функции, возможно, для повторной компиляции;
- для более поздних примеров, реализации могут либо выбратьво всех случаях использовать простые, неэффективные представления закрытой среды или использовать высокопроизводительные представления, но вести учет того, как выглядела среда, чтобы в нее могли быть вставлены замещающие функции;
- в любом случае, для поддержки такого переопределения язык потребует новой функциональности, которая должна быть тщательно указана и сделает язык больше и сложнее.
Оба этих случая действительно говорят о том, что реализации языка либо должны принимать довольно низкую производительность, либо требуют героических приемов, и в любом случае было бы больше возможностей для реализации, чем уже было. Ну, одна из целей усилий Common Lisp заключалась в том, что, хотя героические приемы разрешены, они не должны требовать для высокой производительности. Кроме того, язык уже считался достаточно большим. Наконец, разработчики почти наверняка просто отклонили бы такое предложение: у них уже было достаточно работы, и они больше не хотели, особенно на такого рода уровне «вам нужно будет полностью перестроить компилятор, чтобы сделать это».
Вот почему прагматично то, что вы ищете, невозможно.