From: Barry Fishman
Subject: How are special and lexical binding supposed to interact?
Date: 
Message-ID: <m3snkq35go.fsf@ecube.local>
Given the following code segment:

;;;;;;;;;;;;;;;;;; start segment ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(let ((x 10))
   (defun lfoo () x))

(let ((x 20))
   (declare (special x))
   (defun dfoo () x))

(defparameter x 30)
;(setq x 30)

(terpri)
(princ (let ((x 40)) (lfoo)))
(princ " ")
(princ (let ((x 50)) (dfoo)))
(princ " ")
(princ (let ((x 60)) (declare (special x)) (lfoo)))
(princ " ")
(princ (let ((x 70)) (declare (special x)) (dfoo)))
(terpri)
;;;;;;;;;;;;;;;;;;;; end segment ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Using 4 different lisp implementations I get:

Allegro CL   10 50 10 70
CLISP        40 50 60 70
CMU CL       10 50 10 70
GCL          40 50 60 70

When the defparameter is replaced with just a setq or setf I get:

Allegro CL   10 30 10 70
CLISP        10 30 10 70
CMU CL       10 50 10 70
GCL          10 30 10 70

So that no two of the implementations agree!  I am using the current
release of each of the common lisp environments.

My reading of Paul Graham's "ANSI Common Lisp" pages 112-113, gives me
the impression that the result should be how CMU CL handled it:

             10 50 10 70

The second case (50) was concluded from his example on page 113, since
it doesn't meet the books previously set requirements.  I assumed the
third case (10) should just be a lexically bound situation
irregardless of how x was set in the outer scope. (Shouldn't it?)

The book states "Global varibles established by calling setf at the
top level are implicity special," and that the use of defparameter was
just "cleaner".

What is correct?

To my ear, Paul Graham's text seemed to avoid covering all the cases,
unlike the more complete discussions elsewhere (such as method
precedence later in the book).  Is this due do a lack of specification
in the ANSI spec.  If that is true, what are the implementation
problems?

Barry Fishman

From: Tim Bradshaw
Subject: Re: How are special and lexical binding supposed to interact?
Date: 
Message-ID: <ey3itlmo5h5.fsf@cley.com>
* Barry Fishman wrote:

> So that no two of the implementations agree!  I am using the current
> release of each of the common lisp environments.

I believe that ACL and CMUCL are both right.  For the DEFPARAMETER
case they obviously agree.  For the SETF-at-top-level case the
behaviour is undefined, and CMUCL takes the approach of treating it as
if there had been a DEFVAR/DEFPARAMETER, whereas ACL does not make an
implicit global special declaration, thus treats the binding in the
second case as lexical, and ends up looking at the top-level value in
DFOO.  Both are conforming.

I haven't thought hard about what GCL / CLISP do.

> The book states "Global varibles established by calling setf at the
> top level are implicity special," and that the use of defparameter was
> just "cleaner".

No, that is not right.  You should not establish global variables by
SETF at the top level, indeed it makes no real sense to do so.

I think that ACL's behaviour is nicer, because it avoids the problem
of typing (setf x 3) to the listener and having this destroy your
program.  After years of use of CMUCL I now ritually *never* assign at
the top-level without using the * convention...

--tim
From: David Bakhash
Subject: Re: How are special and lexical binding supposed to interact?
Date: 
Message-ID: <m3u252u29e.fsf@alum.mit.edu>
Tim Bradshaw <···@cley.com> writes:

> > The book states "Global varibles established by calling setf at
> > the top level are implicity special," and that the use of
> > defparameter was just "cleaner".
> 
> No, that is not right.  You should not establish global variables by
> SETF at the top level, indeed it makes no real sense to do so.

Yes.  I recall Graham making a point of using setq and not defvar in
one of his examples (continuations?) since he specifically didn't want 
his [continuation?] to be global.  It was a neat hack, but I think it
would only work properly in some implementations.

> I think that ACL's behaviour is nicer, because it avoids the problem
> of typing (setf x 3) to the listener and having this destroy your
> program.  After years of use of CMUCL I now ritually *never* assign
> at the top-level without using the * convention...

This is indeed the case, and I certainly prefer what ACL does (and
LispWorks too, for that matter).  I don't see why people have trouble
with the idea that a variable can be defined lexically at the
top-level without being special, and kinda wish that the ANSI spec
mentioned this.  If you wanted it special, you should be able to
declare it to be so at the toplevel very easily:

(declaim (special x))

dave
From: Kent M Pitman
Subject: Re: How are special and lexical binding supposed to interact?
Date: 
Message-ID: <sfw7l1x8usj.fsf@world.std.com>
David Bakhash <·····@alum.mit.edu> writes:

> This is indeed the case, and I certainly prefer what ACL does (and
> LispWorks too, for that matter).  I don't see why people have trouble
> with the idea that a variable can be defined lexically at the
> top-level without being special, and kinda wish that the ANSI spec
> mentioned this.  If you wanted it special, you should be able to
> declare it to be so at the toplevel very easily:

A bit of the problem had to do with the question as to whether global
lexicals and global specials must, might, or must not share storage
with global specials.  Frankly, I think some people may not have been
reasoning well about it, as they or others didn't reason well about
lexicals in the first place.  Primarily as an administrative decision
by me, CLHS contains no pointers to X3J13 design documents that failed
rather than passed.  Perhaps I'll correct this some day.  There were a
large number of failed issues, and I didn't want people perusing them
to mistakenly confuse a passed issue with a failed one (bad enough some
passed issues had multiple parts and versions, which people already
get confused about).  Anyway, the problem is not editorial (though I
might have gotten away with a note--even notes on certain topics were
very carefully scrutinized), but technical.  There was a vote on what
disposition to do for global lexicals and the answer was: leave them out.
Jonathan Rees wrote a very nice issue called PROCLAIM-LEXICAL detailing
the issues.  

For historical buffs, I definitely recommend reading the issue as presented
to X3J13 plus my personal notes on how the amendment process and voting
went on this.  I've just thrown together some web pages of that info.

  http://world.std.com/~pitman/CL/Issues/proclaim-lexical.html
From: Pierre R. Mai
Subject: Re: How are special and lexical binding supposed to interact?
Date: 
Message-ID: <87k8628moi.fsf@orion.bln.pmsf.de>
Barry Fishman <·············@acm.org> writes:

> (let ((x 10))
>    (defun lfoo () x))
> 
> (let ((x 20))
>    (declare (special x))
>    (defun dfoo () x))
> 
> (defparameter x 30)
> ;(setq x 30)
> 
> (terpri)
> (princ (let ((x 40)) (lfoo)))
> (princ " ")
> (princ (let ((x 50)) (dfoo)))
> (princ " ")
> (princ (let ((x 60)) (declare (special x)) (lfoo)))
> (princ " ")
> (princ (let ((x 70)) (declare (special x)) (dfoo)))
> (terpri)
> 
> Using 4 different lisp implementations I get:
> 
> Allegro CL   10 50 10 70
> CLISP        40 50 60 70
> CMU CL       10 50 10 70
> GCL          40 50 60 70
> 
> When the defparameter is replaced with just a setq or setf I get:
> 
> Allegro CL   10 30 10 70
> CLISP        10 30 10 70
> CMU CL       10 50 10 70
> GCL          10 30 10 70

Note that CMU CL defaults to proclaiming unknown variables special
upon setting them at the top-level, so that defparameter and setq are
equivalent in this regard at the top-level.  You can change this
behaviour by setting ext:*top-level-auto-declare* to nil, and in that
case CMU CL will also agree with Allegro CL for the second case.

The general consensous on this list is that the standard doesn't
specify the behaviour for referencing a variable at top-level which is
unbound, so that both CMU CL's default behaviour in this case, as well
as ACL's behaviour (to which CMU CL can be switched) are technically
"acceptable", but that it's probably better to behave like ACL does,
rather than the default of CMU CL.

For the first test-cases, I'd expect the behaviour of ACL+CMU CL.
Furthermore since GCL and CLISP both behave consistently with ACL and
CMU CL if you use the compiler instead of their interpreters, I'd
argue that their interpreters are broken, since they let the scope of
the special proclamation creep "upward".

Regs, Pierre.



-- 
Pierre R. Mai <····@acm.org>                    http://www.pmsf.de/pmai/
 The most likely way for the world to be destroyed, most experts agree,
 is by accident. That's where we come in; we're computer professionals.
 We cause accidents.                           -- Nathaniel Borenstein
From: Steven M. Haflich
Subject: Re: How are special and lexical binding supposed to interact?
Date: 
Message-ID: <3AA64FD8.4068BFB3@pacbell.net>
Barry Fishman wrote:

> ;;;;;;;;;;;;;;;;;; start segment ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
> (let ((x 10))
>    (defun lfoo () x))
> 
> (let ((x 20))
>    (declare (special x))
>    (defun dfoo () x))
> 
> (defparameter x 30)

This example is not well formed in a way that you may not be
considering.  defparameter does two things: it declaims the
variable to be globally special, and it sets the value of
the variable.  It is as if its macroexpansion were this:

(progn (declaim (special x))
       (setf x 30))

Therefore, the two preceding defuns in the above example treat
x as a a lexical variable only the first time the code is
executed (or loaded).  If the experiment is rerun in the same
lisp image, when the let forms are reexecuted (or recompiled)
the existing global environment sees the variable x as a
special, and the code does something quite different.
setq does not make the special proclamation, so repeated
execution should yield reproducible results.

> ;(setq x 30)

Another respondent has correctly remarked that ANSI CL gives
no interpretation on free reference to a variable (although
there is a tradition of treating it as a special variable
reference).  You can make your code explicit, and prevent any
additional behind-your-back declarations by making it explicit:

(locally (declare (special x))
  (setq x 30))

If you write it that way, any conformant ANSI CL is required to
do precisely the same thing.  Looking over your results, I suspect
some were run in fresh lisp images, and some were run in images
that previously had a special proclamation for x executed one way
or another.  I suggest replacing the defparameter with the locally
form above, or else rerunning each example in a fresh lisp image.
From: Barry Fishman
Subject: Re: How are special and lexical binding supposed to interact?
Date: 
Message-ID: <m3vgpkdtsd.fsf@ecube.local>
Thanks everyone for your responses.  I feel I have things straight now.

Part of my confusion was (as was pointed out) due to the fact that the
functions were sometimes 'compiled' prior to the special declaration
being read.

I promise I do not write real code like the experimental segment I
gave, and I don't make conclusions about what is correct by what
different language systems choose to do with it. :-)

Barry Fishman
From: Vladimir V. Zolotych
Subject: Re: How are special and lexical binding supposed to interact?
Date: 
Message-ID: <3AAA04CF.57685FE8@eurocom.od.ua>
Barry Fishman wrote:
> 
> .....  I assumed the
> third case (10) should just be a lexically bound situation
> irregardless of how x was set in the outer scope. (Shouldn't it?)

Excerpt ion from CLHS follows

"A special declaration does not affect inner bindings of a var;
the inner bindings implicitly shadow a special declaration
and must be explicitly re-declared to be special."

It may be helpful. Also here is another quote from HS.

(defun hack (thing *mod*)    ;The binding of the parameter
   (declare (special *mod*))  ; *mod* is visible to hack1,
   (hack1 (car thing)))       ; but not that of thing.
 (defun hack1 (arg)
   (declare (special *mod*))  ;Declare references to *mod*
                              ;within hack1 to be special.
   (if (atom arg) *mod*
       (cons (hack1 (car arg)) (hack1 (cdr arg)))))

IMHO it clearly shows difference between lexical and
dynamic variables.

-- 
Vladimir Zolotych                         ······@eurocom.od.ua