[comp.sys.xerox] more comments on defvar

fsbrn@BRL.ARPA (Ferd Brundick, VLD/LTTB) (03/29/88)

Haah,

Last week I posted a letter about the use of defconstant, defparameter,
and defvar.  I've gotten a couple of responses, and the one from Chris
Schmidt pointed me in a new direction.

He says he doesn't agree with my generalization "... that the def
constructs apply to global variables."  He then says "DEFVAR proclaims
a variable special (CLtL, p 68)."  Sure enough, that's what it says in
the middle of page 68.  However, the top line of the page says "The
defvar and defparameter [and defconstant] special forms are the usual
means of specifying globally defined variables [and constants]."  On
page 69 Steele says "These constructs are normally used only as
top-level forms."  I'll get back to that statement later.

I think the real issue is "what are 'special' variables and how are
they used (and misused)?", but first another aside.

The first LISP I was exposed to was Franz, which is dynamically
scoped.  We were given the kernel of a program, and over a period of
time it evolved and grew until only a few of the original function
names remained.  After we bought our first Xerox Lisp machines I
translated everything into Interlisp-D, and about a year ago I rewrote
it again, this time in Gould CL.

Like I said in my previous note, I would bind my "global" variables to
a "main" function and use Franz's dynamic scoping to access these
variables from the subfunctions.  There are far too many variables in
the program to pass them around in argument lists.  When I started
using CL I read that it is lexically scoped, and I got the idea that
variables must be *global* or local.  (When I say *global* I mean the
variables are bound at the top level.  "global" or "semi-global means
they are bound somewhere in the calling sequence and accessed through
the stack.)  I also thought "special" meant *global*.  In my rewrite I
didn't bind the *global* variables to anything and the program worked
the way it always had.

Once I got a copy of Lyric I downloaded the files and got them to work
with only minor changes (mostly OS-dependent).  Then I learned that
"special" means "dynamically scoped" so I started binding "semi-global"
variables the way I did in Franz.  Now I think of (DECLARE (SPECIAL
VARxxx)) to mean "export" in the function that binds VARxxx and
"import" in all the functions that use VARxxx.  (Why didn't Steele use
the words "export" and "import" if they are accurate?)  If no one binds
VARxxx then it is *global*, which is essentially what I had been doing.

How does this affect the Xerox environment?  Again referring to my
earlier note, the vmem is persistant.  If a program creates any
*global* variables they will hang around until they are explicitly
unbound (with MAKUNBOUND), the vmem is recopied, or a (LOGOUT T) is
performed (the last 2 being brute force approaches).  At the Lyric
conversion course I attended the instructor pointed out that *global*
variables are a Bad Thing in a multi-processing environment.  How can
you run multiple copies of a program if they all use the same *global*
variables?  I have modified a module (the changes were fairly minor)
to use a hash table instead of property lists to store lots of data.
Not only can I bind the hash table so multiple processes may be run,
but I don't have to worry about the data structures being garbage
collected after I'm done (there aren't any multiple references).

The issue now seems to be largely a matter of style.  If you think the
use of *global* or "global" variables is a Bad Thing this doesn't
affect you.  Property lists are by nature *global*, which is why I've
switched to hash tables.  What is the "preferred" way (or your
favorite way, I suppose) to bind and reference global variables?
I used to append a program's name to each of its functions so I could
keep track of them and not overwrite another program's function with
the same name.  While this is still done in the Library and Lispuser
modules, packages are one way around the problem.  However, this
doesn't help global variables with multiple copies of the same program.
Yes, another technique is to use property lists on a program's main
window, which is what I use for *some* of my variables.  I don't want
the overhead of GETing and PUTing properties every time I need to
see or modify a value.

My two main sources for CL information are Steele's book (CLtL) and
Tatar's book, _A Programmer's Guide to CL_.  Chris and I have already
mentioned what Steele says about the "def" macros.  His description of
"special" (pp 157-8) says "All variable bindings affected are made to
be dynamic binding rather than the current local binding."  He then
gives a simple example and a very contorted one.  On pp 76-7, Tatar
says "Variables are lexically scoped unless they are declared special,
in which case they are dynamically scoped."  She then gives a couple
of examples similar to Steele's simple one.  Ok so far.  After the
examples she says (p 78) "Special variables are not often used in this
form [accessing dynamic variables on the stack].  However, there is a
generalization of the 'special variable' idea which is used frequently.
So-called global variables in Lisp are, in fact, dynamically scoped
special variables."  This is her intro to the "def" macros, the
"top-level variable declarations" and free variables.

Now I've come full circle.  Tatar says to use def* to declare global
free variables, but I don't want free vars so I can run multiple
copies.  Is it safe to say that both Steele and Tatar are assuming a
single process, static environment, and that the Xerox environment
requires new rules and techniques?

As an experiment I created two functions, FOO and BAR.  FOO binds a
variable in a LET, tries to DEFVAR it, and calls BAR.  BAR tries to
access the variable.  The results I got when I ran this are so weird
I'm sure that I tried to use defvar in a nonsensical manner.  *IF*
what I did makes no sense, then it is not strictly true that def*
means the same as "special".  It also makes me wonder if you can
defvar a variable anywhere *except* at the top level, even though
Steele claims "These constructs are normally used only as top-level
forms."

Do I have a gross misunderstanding of how these things are "supposed"
to work, or am I just making an issue out of nothing?  I haven't read
all of Tatar's book (yet) and I haven't seen any "real" examples of
how and when to use defvar vs. dynamically scoped semi-global
variables.  Is Tatar wrong when she says "special" variables are
rarely used?  I looked at her programs near the back of the book and
she prefers to use defvar at the top level for all her *global*
variables and never says (DECLARE (SPECIAL VARxxx)).  [When I try to
compile functions that reference free variables the compiler warns me
that it is assuming the variable is special.  Putting in a DECLARE
SPECIAL statement silences the compiler.  Running interpreted doesn't
seem to care.  This happened on all 3 CLs I tried.]  The book _Common
LISPCraft_ doesn't even talk about special variables or def*.  (In my
opinion the book is a minor rewrite of _LISPCraft_ using CL function
names instead of Franz names.  A lot of CL's power is ignored.)

Here is a dribble file of the test I tried under Lyric.  It shows the
functions FOO and BAR, 2 sample calls to FOO, a modification of FOO,
and another call to FOO.  What is supposed to happen is FOO binds Y to
itself, makes it "special" with defvar, then calls BAR which should
see the binding of Y on the stack.  That happens in the second and
third calls but not the first.  I try to get the value of Y at the end
to show that it isn't just a *global* value.

263> pp foo
FUNCTIONS definition for FOO:

(DEFUN FOO (X) 
   (LET (Y)
      (DEFVAR Y '(INITIAL VALUE))
      (FORMAT T "foo: ~A  ~A~%" X Y)
      (BAR X)))

264> pp bar
FUNCTIONS definition for BAR:

(DEFUN BAR (X2)
   (FORMAT T "foo: ~A  ~A~%" X2 Y))

265> foo(alpha)
foo: ALPHA  (INITIAL VALUE)
Unbound variable: Y.

266> foo(beta)
foo: BETA  NIL
foo: BETA  NIL
NIL
267> ed(foo)
Editing FUNCTIONS definition of FOO
FOO
268> pp foo
FUNCTIONS definition for FOO:

(DEFUN FOO (X)
   (LET (Y)
      (DEFVAR Y '(INITIAL VALUE))
      (SETQ Y '(NEW VALUE))
      (FORMAT T "foo: ~A  ~A~%" X Y)
      (BAR X)))

269> foo(gamma)
foo: GAMMA  (NEW VALUE)
foo: GAMMA  (NEW VALUE)
NIL
270> y
Unbound variable: Y.

                                        dsw, fferd
                                        Fred S. Brundick
                                        USABRL, APG, MD.
                                        <fsbrn@brl.arpa>