From: Jim Meehan
Subject: Re: Question about dynamic scoping
Date: 
Message-ID: <1991Nov18.103920.27011@src.dec.com>
    Global variables in Common Lisp are special variables...
    
Not true.  Example:

    (setq a 0)
    (defun f () (print a))
    (let ((a 1)) (f))

That will print 0, not 1, because 'a' is global and not special.
Nor are all special variables global.  

    (defun g () (print c))
    (let ((c 0)) (declare (special c)) (g))

That will cause an error ("unbound variable c").

From: Danny Brewer
Subject: Re: Question about dynamic scoping
Date: 
Message-ID: <173@farallonfarallon.com>
In article <······················@src.dec.com>, ······@src.dec.com (Jim Meehan) writes:

> Nor are all special variables global.  
> 
>     (defun g () (print c))
>     (let ((c 0)) (declare (special c)) (g))
> 
> That will cause an error ("unbound variable c").
> 
I was surprised to read this.  I wouldn't expect an error, but would
expect zero to be printed.  I tried this in Macintosh Common Lisp 2.0b1p3,
and indeed zero was printed.  Although when I entered the first line,
I did get a compiler warning about c being an undeclared free variable.

Setting up a global variable [i.e. (SETQ c 0) ] would establish a lasting
binding for c.  The LET form in the second line establishes a binding on
c of dynamic extent [i.e. lasts as long as the LET form].  Therefore, (g)
is able to refer to that special variable, even though it wasn't declared
as special in (g).  But once the LET form goes away, there is no more binding
on c, and so (g) would cause an error outside of the LET.
From: Jim Meehan
Subject: Oops. Re: Question about dynamic scoping
Date: 
Message-ID: <1991Nov19.210527.27357@src.dec.com>
Jeff Dalton is quite right; I stand corrected.  Even in CLtL I (page
55), global references refer to special variables.  I never said I
thought that non-global specials were a good IDEA -- indeed, I think
ALL top-level setq's are in bad taste (and even some that aren't at
top-level :-) -- but I did think they were possible, even if every
compiler warned you about them.  Live and learn; thanks.
From: Jeff Dalton
Subject: Re: Question about dynamic scoping
Date: 
Message-ID: <5654@skye.ed.ac.uk>
In article <······················@src.dec.com> ······@src.dec.com (Jim Meehan) writes:
>
>    Global variables in Common Lisp are special variables...
>    
>Not true.

I'm sorry, but it is true, and I can show it using your examples
and some others.  Really.  That they are special is why there
was a PROCLAIM-LEXICAL cleanup (not passed).  However, this is
a confusing issue that is not widely understood, so I will have
to explain at some length.

Let me start by saying that you are more or less right if we take
"special variable" to mean "variable that was proclaimed special, eg
by DEFVAR".  But there can be special variables that are special
locally, as a result of a declaration, rather than globally and
pervasively as the result of a proclamation.  C in your second
example (example 2 below) is an example.  Global variables that
have not been proclaimed special are also special in that sense.

First, look at CLtL II, page 70, the last full paragraph:

  ... if the symbol occurs textually within a program construct
  that creates a _binding_ for a variable of the same name, then
  the reference is to the variable specified by the binding; if
  no such program construct textually contains the reference,
  then it is taken to refer to the special variable of that name.

Consequently, in a top-level (DEFUN F () A), the symbol A, which we
agree is a reference to the global variable A, "is taken to refer to
the special variable of that name".  Hence, the global A is "the
special variable of that name".

Some (most?) Common Lisp compilers give a warning for such functions
if the variable has not been declared (or proclaimed) special, because
they think you may not have meant to refer to the global.

Example 1:

>    (setq a 0)
>    (defun f () (print a))
>    (let ((a 1)) (f))
>
>That will print 0, not 1, because 'a' is global and not special.

The reason it prints 0 is because A is not special in
(LET ((A 1)) (F)), and _not_ because the global A is not
special.  The reason A is not special in (LET ((A 1)) (F))
is it has not been proclaimed or declared special.

We can't test what would happen if the global variable A
were declared (but not proclaimed) special, because DECLARE
isn't allowed at the top level.  But we can do a similar
experiment with local variables acting as globals.  See 5 below.


Example 2:

>Nor are all special variables global.  
>
>    (defun g () (print c))
>    (let ((c 0)) (declare (special c)) (g))
>
>That will cause an error ("unbound variable c").

Have you actually tied it?  It should print 0.  To make sure I hadn't
misunderstood things I tried in the past, I have just checked by
trying it in:

  CMU Common Lisp 15a
  Sun Common Lisp, Development Environment 4.0.0 , 6 July 1990
  Allegro CL 4.0.1 [Sun4] (2/8/91)
  AKCL (Austin Kyoto Common Lisp)  Version(1.505)

Note that many Common Lisps will give you a _warning_ when the
function G is processed.  That is because C has not been proclaimed
special, and the compiler thinks you may have made a spelling error.
CMU CL will give you the warning when G is compiled, or when G is
first called.


Example 3:

On CLtL II, page 119, we find:

  SYMBOL-VALUE <symbol>
  
  SYMBOL-VALUE returns the value of the dynamic (special) variable
  named by <symbol>.

Try this:

  (setq a 0)
  (symbol-value 'a)


Example 4:

  (setq a 0)

  (let ((a 1))
    (list a        ;ref to lexical A
          (locally (declare (special a))
            a)))   ;ref to special A

The result will be: (1 0).


Example 5:

When some people explain global variables, they say "it's like a
big LET wrapped around the program [in which DEFUNs are replaced
by a LABELS inside the LET]."  What it's more like is "a big LET
with all variables declared special wrapped around the program."

That is, if you compare the behavior of global variables with local
variables, you will find that they behave like local variables that
have been declared special and not like local lexical variables.

This example uses only local variables but otherwise does the same
sorts of things as examples 1 and 2.  The outermost local variables
are used as a "global" environment.

  (let ((a 0)				;"global" special
        (b 0))				;"global" lexical
    (declare (special a))

    ;; These functions refer to the variables acting as globals,
    ;; that is to the variables bound by the LET above.
    (flet ((ref-a () a)
           (ref-b () b))

      ;; Now we will try rebinding A and B, first as lecical
      ;; variables, then as special variables.
      (list

        ;; This is like example 1 twice, once for a
        ;; special (A) and once for a lexical (B).
        ;; We make a local lexical binding and see if
        ;; the global reference "sees" it.
	(let ((a 1) (b 1))
          (list 'test-1 (ref-a) (ref-b)))

        ;; This is like example 2 twice, once for a
        ;; special (A) and once for a lexical (B)
        ;; We make a local special binding and see if
        ;; the global reference "sees" it.
        (let ((a 2) (b 2))
          (declare (special a b))
          (list 'test-2 (ref-a) (ref-b))))))

The result is ((TEST-1 0 0) (TEST-2 2 0)).

Note that test-1 fails to distinguish between special and lexical.
That's why Jim Meehan and I can agree on the result but disagree
on the reasons.

In test-2, however, we find that the ref to the special "global"
(REF-A) is affected by the local special binding while the ref
to the lexical "global" (REF-B) is not.

If global variable weren't special, they'd behave like B above.
Instead, they behave like A.

-- jeff