[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Defun inside Let

S> Date: Thu, 30 Jan 1986  14:29 EST
S> From: "Scott E. Fahlman" <Fahlman@C.CS.CMU.EDU>
S> when a Defun occurs inside a Let or anywhere else, it changes the
S> GLOBAL function definition for the symbol in question.  It is not a
S> form of LABELS.

I agree, see my related opinion below.

D> Date: Thu, 30 Jan 86 16:08 EST
D> From: David C. Plummer <DCP@SCRC-QUABBIN.ARPA>
D> (let ((a 3) (b 5))
D>   (defun add-and-increment ()
D>     (prog1 (+ a b) (incf a) (incf b)))
D>   (defun sub-and-decrement ()
D>     (prog1 (- a b) (decf a) (decf b))))

Clearly that creates two global function-bindings (definitions) to
function-objects each of which has access to the same lexical
environment (the one containing a and b initialized to 3 and 5 resp.).
I think we all agree so far.

D> That's all fine and dandy.  Now come the hard parts.  
D>  - What if I want to redefine add-and-increment to be
D>      (defun add-and-increment (&optional amount)
D>        (prog1 (+ a b) (incf a amount) (incf b amount)))

If you just do a toplevel defun, you are replacing the old definition
with a new one that uses the null lexical environment instead of the
one with a and b used by the earlier definitions. Thus you are
probably making a mistake, doing something other than what you
obviously wanted to do. The old function-object still accesses the a,b
environment but is inaccessible, while the new function-object
currently function-bound to the symbol add-and-increment access the
null environment instead. But if you had explicitly recovered the
lexical environment from either add-and-increment or
sub-and-decrement (either will do in this case), and executed the new
DEFUN in that environment, then the new function-object could share
lexical a,b once again. In this particular example you can recover
from the mistake because even though the old add-and-increment
function-object is inaccessible so you can no longer recover the a,b
environment from it (unless your user interface has a history/undo
mechanism), you can still recover the environment from the
not-yet-lost sub-and-increment function-object which is still
function-bound to the sub-and-increment symbol thus accessible.
(However I don't know how to actually obtain explicit CLtL-level
access to that lexical environment; in earlier debate on macro
expansion didn't we decide that at present CLtL provides no such
mechanism for explicitly handling &environment stuff but ought to be
changed to provide it?)

D> Is the system expected to know that >the old version< of
D> add-and-increment is a lexical closure over some variables and make the
D> new one share that same environment?

I'd say definitely not. If you specify null lexical environment, by
doing a toplvel DEFUN, that's what you should get.

D> - What if I wanted to add a variable to the lexical environment?  What
D>    forms to I type to splice something in?

I'm under the impression lexical environments (except possibly the
null environment) can't get new variables once they are formed, but
there were suggestions to change this both for debugging reasons and
for consistency with the null environment.
D> - What if I want to redefine add-and-increment to be interpreted?  Are you
D>    forcing all implementations to have the same compiler and interpreter
D>    environment?

Now the cans of worms really start to open. I'd say if there is a
mechanism for redefining functions inside a non-null lexical
environment, as in the a,b example above, or if by any other mechanism
it is possible to create a situation where some but not all functions
in a particular lexical environment are compiled, this implies that
both interpreted and compiled functions must access lexical
environments in the same way essentially, i.e. that there be just one
implementation of lexical environment that is used by both kinds of
code. (Well, it would maybe be possible to have two kinds of lexical
environments, one optimized for compiled code and one not, but allow
either kind of function to access either kind of environment, sort of
like the way interpreted functions can call compiled functions by
calling APPLY on the contents of the function cell and vice versa by
linking to COMPILED-CALLING-INTERPRETED or whatever which sets up a
call to APPLY. But then if you initially load some functions
interpreted and later compile them in memory you may be permanently
stuck with the compiled functions running slowly as they access the
non-compiled lexical environment.)
D> My personal feeling is that DEFUN that requires a
D> non-null lexical environment, such as created by LET, is a timebomb
D> ticking quickly.

Well, if the language allows the DEFUN to be done, but the implementor
doesn't take care to do things correctly, indeed poor programmers will
find their code blowing up whenever they trigger the bugs.

One major question for CL advocates. Has anybody written a completely
correct implemention of CL, with no exceptions whatsoever, with all
these esoteric/messy cases correctly handled, with both interpretor
and compiler doing the expected things, so that we can establish by
example that CLtL isn't self-contradictory?? I personally believe that
as currently documented CLtL is indeed self-contradictory, even after
all the typos have been fixed, and thus in some sense hopeless as a
standard, but I'd be glad if I was wrong.