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

Re: Side effecting constants inside functions



> From: Evan Kirshenbaum <evan@SU-CSLI.ARPA>
> Subject: Re: Side effecting constants inside functions
> 
> ...
> (defmacro compute-once (form)
>   (let ((cell (cons nil nil)))
>     `(if (car ',cell) (cdr ',cell)
> 	 (setf (car ',cell) ,form))))
> ...

All these proposed mechanisms work in compiled code, but CLtL
intentionally [?] provides no guarantee that a macro will be expanded
only the first time an interpreted function is evaluated.  Questionable
macro hacks like these are a wonderful tool to retain a few
unpredictable differences between compiler and interpreter semantics in
the language ;-).

The original issue was whether modification of code or internal
constants in code ought be defined in the language.  I plead all code
modification should be `an error', but with no guarantee that the error
will be signalled.  (In particular, the practice might happen to work
in some implementations.)  This is exactly the situation now.  The only
change would be to the manual.  Below is an attempt at a clean formal
statement:

=====

It is an error to attempt subsequently to modify the closure created
when the @I(function) special form is passed a lambda expression.  (It
may, of course, be discarded.)  For these purposes, defun, defmacro,
and friends such as defstruct may all be considered to create functions
by evaluating a @I(function) special form.

Note: The function created by defmacro, of course, is not the macro
itself, but the unnamed lambda stored in the macro-function cell of the
named symbol.

=====

I anticipate argument that this restriction makes impossible all the
neat code modification hacks for which lisp is famous.  So be it.
Hacking the insides of a lambda has never worked for compiled code, and
(p.143):
	an implementation of Common Lisp has great latitude in
	deciding exactly when to expand macro calls inside a program.
	...  (An implementation might even choose always to compile
	functions defined by @B(defun), even when operating in an
	"interpretive" mode!)

It is but a small additional step to permit "(function (lambda ... ))"
also to invoke the compiler, and on this CLtL is silent.  (I admit the
performance issues might be horrendous in some cases.)  Remember,
messing with the innards of a lambda, for example, changing the
identity of lexically referenced variables, completely invalidates any
lexical analysis performed by @I(function).  Read p.89 for details.  If
one feels it is absolutely necessary to modify code, the correct way to
do it is by passing a form to an explicit call to eval.

A final side issue:  Perhaps CLtL should provide a plausible *sketch*
for the defun macro.  This would make clearer to the neophyte the
connection between @I(defun) and @I(function).  Something simple,
ignoring any messy complications, like:

	(defmacro defun (name lambda-list . body)
		  `(setf (symbol-function ,name)
			 (function (lambda ,lambda-list ,@body))))