From: Francis Sergeraert
Subject: Re: Closure around defun
Date:
Message-ID: <CL9GA3.GrA@imag.fr>
The point is that your *print-length* is implicitly "special" because
declared in a
(DEFVAR *PRINT-LENGTH* nil)
at the initialization of Lisp.
Do the following experience:
> (setf x 10) ==>
10
> (let ((x 100))
(defun bar () x)) ==>
BAR
> (bar) ==>
100
> (defvar x) ==>
X
> x ==>
10
> (let ((x 100))
(defun bar () x)) ==>
BAR
> (bar) ==>
10
>
Why these strange facts ? My personal model (is it right ??) is that
at any time you have the global-environment-stack and also the
local-environment-stack. When Lisp enters a let, it adds the new
value, sorry the new binding in the official terminology, on the local
stack. If a closure is created, this local stack is keeped in this
closure. This was the case for the first definition of bar above.
But if the variable is special, this new binding, sorry this
value..., is added on the global-environment. The local environment is
saved inside a created closure, but it does not contain this new
value. Secondly when the let is finished, this new value is removed
from the global-environment. This is better showed in the following
extended examples. Don't forget that evaluating a symbol returns the
binding (local value) if defined, and on the contrary (symbol-value
...) returns the global one only and the highest one if several have
been defined.
> (setf y 10) ==>
10
> (let ((y 100))
(print y)
(print (symbol-value 'y))
(defun bar () y)) ==>
100
10
BAR
> (bar) ==>
100
> (defvar y) ==>
Y
> y ==>
10
> (let ((y 100))
(print y)
(print (symbol-value 'y))
(defun bar () y)) ==>
100
100
BAR
> (bar) ==>
10
>
Look also at this example:
> (let ((z 10))
(declare (special z))
(defun foo () z)) ==>
FOO
> (foo) ==>
ERROR : z is unbound.
The value of z is lost because it was available only on the
global-stack and was not stored inside the closure.
And also:
> (let ((a 10))
(declare (special a))
(defun foo () (bar))) ==>
FOO
> (let ((a 100))
(defun bar () a)) ==>
BAR
> (foo) ==>
100
>
because the a of the second let is pushed on the local-stack, therefore
keeped inside the definition of bar.
Personnally I regret this model (or the right one) is not described
int the CLtLn; this would make much simpler the understanding of these
very esoteric notions of bindings vs values, extents vs scopes, etc.