Jeff M. <massung@gmail.com> wrote:
+---------------
| Lexical bindings can very often be optimized to a stack frame address
| and sometimes even a register, based on usage in the code and the
| optimizing power of the compiler (and SBCL is one of the best!).
+---------------
Indeed. However, lexical bindings can't be optimized into stack frame
addresses if they're shared between independent closures *and* are
mutated. [Note that if they're only shared and never mutated, the
bindings can safely be "split" and separate copies stored with each
closure.] In that case, the lexical variable must be evicted into the
heap. Implementations such as MzScheme and CMUCL [and presumably SBCL]
do this by rewriting such variables into indirect references to "box"es,
such that the binding between the "variable" and the box is again constant
[even though the box's contents are mutable], which permits sharing and/or
splitting/copying of the binding. For example, give this code:
(defun make-account (&optional (initial-balance 0))
(let ((balance initial-balance))
(values (lambda () balance) ; a "getter"
(lambda (increment) ; a "setter" [well, an "incrementer"]
(incr balance increment)))))
if can be re-written as:
(defun make-account (&optional (initial-balance 0))
(let ((balance (make-box :value initial-balance)))
(values (lambda () (box-value balance))
(lambda (increment)
(incr (box-value balance) increment)))))
and now the "balance" binding is read-only again
[after the initial binding] and can be safely shared/split.
-Rob
-----
Rob Warnock <rpw3@rpw3.org>
627 26th Avenue <URL:http://rpw3.org/>
San Mateo, CA 94403 (650)572-2607