Теперь ваши функции ссылаются на переменную, которая не гарантируется для определения.Попытка выполнить (foo)
в ответе приведет к ошибке несвязанной переменной.Мало того, что есть ссылочная непрозрачность, но теперь ошибка выброса ссылочного контекста !
Здесь вы видите глобально связанные подпрограммы, которые могут быть выполнены только в локальном контексте, где (declare (special x))
былнамекал.Вы также можете поместить эти функции в labels
, чтобы они случайно не использовались, хотя в этот момент вы выбираете между закрытием переменных в функциях или закрытием функций в функции:
(defun main ()
(labels ((fum ()
(let ((x 1));Inadvertent use of x?
(setf x 2))
(foo))
(foo ()
(declare (special x))
(bar x))
(bar (arg) arg)) ;Final consumer of x.
(let ((x 0))
(declare (special x))
(fum))))
Ух ты, какой-то уродливый код!
После свертки мы можем сделать x
лексическим!Теперь мы можем достичь Святого Грааля, ссылочной прозрачности!
Convolute
(defun main ()
(let ((x 0))
(labels ((fum ()
(let ((x 1))
(setf x 2))
(foo))
(foo () (bar x))
(bar (arg) arg));Final consumer of x.
(fum))))
Этот код намного приятнее и шустрее.По сути это ваш код для другого вопроса , но привязки функций локализованы.Это по крайней мере лучше, чем использование взрывного глобального именования.Внутренняя пусть ничего не делает, как и прежде.Хотя теперь он менее запутанный.
CL-USER> (main) ;=> 0
Ваш тестовый пример одинаков (main) ;=> 0
в обоих.Принцип заключается в том, чтобы просто лексически инкапсулировать ваши переменные вместо динамических объявлений special
.Теперь мы можем еще больше сократить код, просто передав функциональность в одну переменную среды, , как предложено .
(defun convoluted-zero ()
(labels ((fum (x)
(let ((x 1))
(setf x 2))
(foo x))
(foo (x) (bar x))
(bar (arg) arg)).
(fum 0)))
CL-USER> (let ((x (convoluted-zero)))
(list x (convoluted-zero)))
;=> 0
□ QED ваш кодсо специальными переменными нарушает абстракцию.
Если вы действительно хотите пройти по кроличьей норе, вы можете прочитать раздел главы 6 Дуга Хойта Let Over Lambda о пандорических макросах, где вы можете сделать что-то вроде этого:
(use-package :let-over-lambda)
(let ((c 0))
(setf (symbol-function 'ludicrous+)
(plambda () (c) (incf c)))
(setf (symbol-function 'ludicrous-)
(plambda () (c)(decf c))))
Затем вы можете использовать pandoric-get
, чтобы получить c, не увеличивая его или не определяя любую функцию доступа в этом контексте, что является абсолютным помешательством.С пакетами lisp вы можете избежать локальной глобальной переменной.Например, я могу увидеть приложение для этого в elisp, в котором нет встроенных пакетов.