In article 4CBC918B-9B18-4C07-B289-A95040C33919@gerg.org, Greg Gilley ggilley@gerg.org wrote:
There are some tests in the common-lisp test suite with dynamic binding that I don't understand. If someone could help shed some light on them I'd appreciate it.
progv makes it's arguments special. I don't understand how they can be a different special than the one declared in the let. I'd love an explanation.
(let ((x 0)) (declare (special x)) (progv '(x) () (boundp 'x))) ==> NIL
(let ((x 0)) (declare (special x)) (progv '(x) () (setq x 1)) x) ==> 0
Thanks,
Greg
Notice that PROGV does *not* make a binding special. If you want to do that at run-time, you have to use
(proclaim `(special ,name))
For illustration, consider the following case:
CL-USER> (progv '(.x.) '(:progv) (let ((.x. :inner-let)) #'(lambda () .x.))) #<FUNCTION (LAMBDA ()) {CDA3DCD}> CL-USER> (let ((.x. :outer-let)) (funcall *)) :INNER-LET
PROGV can /basically/ be thought of as being a macro that expands to
(let ((old-values (mapcar #'symbol-value <VARS>))) (unwind-protect (progn (mapc #'(setf symbol-value) <VALS> <VARS>) . <BODY>) (mapc #'(setf symbol-value) old-values <VARS>)))
It's more complicated than that but I think it suffices as a mental model.
In case of multi-threading, its implementation is even more complicated because it "must" be ensured that PROGV establishes thread-local bindings. (The quotes around "must" because multi-threading is, as you probably know, not part of the ANSI standard.)
Consider:
CL-USER> (defparameter *foo* :global) *FOO* CL-USER> (sb-thread:make-thread #'(lambda () (progv '(*foo*) '() (setq *foo* :thread)))) #<SB-THREAD:THREAD RUNNING {CEEBFA9}> CL-USER> (sb-thread:join-thread *) :THREAD CL-USER> *foo* :GLOBAL
Regards,
-T.