Subject: Re: (Stupid) Implementation-dependent CL tricks ?
From: rpw3@rpw3.org (Rob Warnock)
Date: Fri, 13 Mar 2009 21:51:13 -0500
Newsgroups: comp.lang.lisp
Message-ID: <UZ6dnal3wMc8hibUnZ2dnUVZ_oTinZ2d@speakeasy.net>
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