Sam wrote:
> Eli Bendersky wrote:
> >
> > Consider this code:
> >
> > (defun printer (val)
> > (lambda () (format t "~a~%" val)))
> >
> > (setq printer-of-10 (printer 10))
> > (funcall printer-of-10)
> >
> > It prints "10" as expected. However, consider this code:
> >
> > (defvar val 12)
> > (funcall printer-of-10)
> >
> > It prints 12 !
>
> this is, of course, not compliant.
You mean, clisp's result are not ANSI CL compliant? Nah. The program
above is not conforming ANSI CL, therefore clisp can produce arbitrary
results.
Why is it not conforming ANSI CL? Because of section
3.2.2.3 Semantic Constraints:
"All conforming programs must obey the following constraints ...:
Special proclamations for dynamic variables must be made in the
compilation environment."
The code shown above has a SPECIAL proclamation for the variable VAL
in the execution environment (before the funcall) but not in the
compilation environment: at the moment the PRINTER function is defined,
is it not known as a SPECIAL variable. Therefore the code is not conforming.
The same holds also for macros. Before defining a function that uses a macro,
that macro must have been defined.
> ISTR that this behavior was implemented intentionally to ease
> interactive development, because "usually" the printer above would be
> followed by a (forgotten) defvar.
Yes, exactly. When a user compiles a program, the compiler is allowed to
remember the information whether a variable was SPECIAL or not, because
that allows the compiler to generate more efficient code. But in
interpreted code, when the user changes the state of a variable, or
redefines a macro, he does *not* want to re-evaluate all DEFUNs that
use the variable or macro.
ANSI CL gives the implementation freedom regarding interpreted evaluation,
how much it wants to remember / cache, and how much it wants to evaluate
according the current environment, if the environment has changed. clisp
implements "ad-hoc lookup" for variables, but not for macros.
It would be useful to change clisp to take into account changed macro
definitions during the interpretation of function bodies. It currently does
not do so, for efficiency reasons: macro expansion is slow. But with a more
intelligent caching mechanism, this could be remedied. If the interpreted
function would not only cache the function body's expansion but also the
vector of global macros that this expansion depended on, clisp could
decide to throw away the cached macro expansion and redo the expansion
when it sees that a macro's definition has changed.
Test case:
(defmacro pair (x y) `(cons ,x ,y))
(defun foo (a b c d) (pair (pair a b) (pair c d)))
(foo 1 2 3 4) => ((1 . 2) 3 . 4)
; user not satisfied with the result
(defmacro pair (x y) `(list ,x ,y))
(foo 1 2 3 4) => ((1 2) (3 4))
Bruno

Eli Bendersky wrote:
>> on a second thought (and after looking at the source code :-), I think I
>> can explain the CLISP behavior better.
>>
>> this is _interpreted_ code.
>> when printer-of-10 is executed and val is evaluated (after defvar!), we
>> see a globally special symbol val which can have only a global
>> symbol-value (not a local binding), and thus we are compelled to
>> evaluate it to 12.
>>
>> this is an artifact of interaction between global special declarations
>> and code interpretation. the compiled code does not exhibit this
>> behavior because the compilation eliminates the symbol val from the
>> compiled code, so defvar cannot affect it.
>>
>> note that
>> (let ((val 42)) (funcall printer-of-10))
>> prints 10 before defvar and 42 after.
>>
>
> What is the verdict, though ? CLISP, in interpreted mode, still
> doesn't comply to the Common Lisp standard here, or so it seems to me.
CLISP offers you an opportunity to run your code in a fully interpreted
mode. "fully interpreted" makes the current behavior a necessity.
sometimes this behavior is desirable, sometimes it is not, but this is
the way it is, and it is not likely to change.
if you can't live with it, always compile your code.
> By the way, this code is a washed down portion of code that had a very
> insidious bug that hunted me for hours. Imagine writing a large body
> of code, and later define a defvar for some local test with the wrong
> variable name and BOOM - all the code starts working in a really weird
> way after the defvar.
I feel your pain. :-(
nevertheless, I do not think CLISP is to blame here.
Sam.

On 10/8/07, Eli Bendersky <eliben@...> wrote:
> By the way, this code is a washed down portion of code that had a very
> insidious bug that hunted me for hours. Imagine writing a large body
> of code, and later define a defvar for some local test with the wrong
> variable name and BOOM - all the code starts working in a really weird
> way after the defvar.
And why do you think it is recommended to use asterisks around the
name of special variables?
P.

> on a second thought (and after looking at the source code :-), I think I
> can explain the CLISP behavior better.
>
> this is _interpreted_ code.
> when printer-of-10 is executed and val is evaluated (after defvar!), we
> see a globally special symbol val which can have only a global
> symbol-value (not a local binding), and thus we are compelled to
> evaluate it to 12.
>
> this is an artifact of interaction between global special declarations
> and code interpretation. the compiled code does not exhibit this
> behavior because the compilation eliminates the symbol val from the
> compiled code, so defvar cannot affect it.
>
> note that
> (let ((val 42)) (funcall printer-of-10))
> prints 10 before defvar and 42 after.
>
What is the verdict, though ? CLISP, in interpreted mode, still
doesn't comply to the Common Lisp standard here, or so it seems to me.
By the way, this code is a washed down portion of code that had a very
insidious bug that hunted me for hours. Imagine writing a large body
of code, and later define a defvar for some local test with the wrong
variable name and BOOM - all the code starts working in a really weird
way after the defvar.
Eli

Sam Steingold wrote:
> Eli Bendersky wrote:
>> Consider this code:
>>
>> (defun printer (val)
>> (lambda () (format t "~a~%" val)))
>>
>> (setq printer-of-10 (printer 10))
>> (funcall printer-of-10)
>>
>> It prints "10" as expected. However, consider this code:
>>
>> (defvar val 12)
>> (funcall printer-of-10)
>>
>> It prints 12 !
>
> this is, of course, not compliant.
> to get the compliant behavior, you will need to compile the code,
> e.g., by adding (declare (compile)) to the printer definition.
>
> ISTR that this behavior was implemented intentionally to ease
> interactive development, because "usually" the printer above would be
> followed by a (forgotten) defvar.
on a second thought (and after looking at the source code :-), I think I
can explain the CLISP behavior better.
this is _interpreted_ code.
when printer-of-10 is executed and val is evaluated (after defvar!), we
see a globally special symbol val which can have only a global
symbol-value (not a local binding), and thus we are compelled to
evaluate it to 12.
this is an artifact of interaction between global special declarations
and code interpretation. the compiled code does not exhibit this
behavior because the compilation eliminates the symbol val from the
compiled code, so defvar cannot affect it.
note that
(let ((val 42)) (funcall printer-of-10))
prints 10 before defvar and 42 after.
hth.
Sam.