Реализация этой идеи на Common Lisp весьма тривиальна:
(defun make-preserve-context ()
(make-hash-table))
(defun context-add-variable (context symbol)
(setf (gethash symbol context)
(symbol-value symbol)))
(defun context-remove-variable (context symbol)
(remhash symbol context))
(defun context-symbol-value (context symbol)
(gethash symbol context))
(defun (setf context-symbol-value) (newval context symbol)
(setf (gethash symbol context)
newval))
(defmacro with-context (context &body body)
`(let ((cntx ,context))
(if cntx
(let ((symbols)
(values))
(iter (for (s v) in-hashtable cntx)
(push s symbols)
(push v values))
(progv symbols values
(unwind-protect
(progn ,@body)
(iter (for s in symbols)
(setf (gethash s cntx)
(symbol-value s))))))
(progn ,@body))))
Теперь можно создать "защищённый контекст", добавить в него несколько ранее объявленных глобальных переменных и изолировать с помощью макроса with-context код, оперирующий этими переменными, от остального мира.
Вроде бы, похожую вещь делает ContextL (http://common-lisp.net/project/closer/contextl.html).
ОтветитьУдалитьНе, ContextL делает что-то другое, что я толком даже не понял, хотя и не стремился особо понять, ибо подобные системы меня сразу отталкивают...
ОтветитьУдалитьЯ бы сделал так:
ОтветитьУдалить(defmacro with-context ((context (&rest vars)) &body body)
(let ((ctx-var (gensym)))
`(let ((,ctx-var ,context))
(symbol-macrolet
,(loop :for var :in vars
:collect `(,var (context-symbol-value ,ctx-var ',var)))
,@body))))
Но это дело вкуса. Можно сделать два варианта с разными именами на тот случай, если понадобится лексическое связывание переменных контекста.