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

"lexical" globals?



Distinguishing dynamic variables from truly global variables came up at
Lucid last spring, and I think (but am not fully sure) that what you
are calling "lexical globals" is subsumed by the general issue of having
a GLOBAL declaration/proclamation.   I've appended to the end of this note 
the paper I circulated back then to a couple dozen people.  Since it wasn't 
a pressing issue, we didn't raise it in the mailing list, although I spoke,
verbally, at length with a few more people about the problem [in particular, 
GLS, since I had imagined that ThinkCo would be interested in deep-bound 
implementations of Common Lisp].

The reason that I'm not 100% sure that simple global variables cover what
you are calling "lexical globals" is the volume of discussion about
   (let ((a <some-value>))
     (defun foo ...)
     (defun bar ...))
In this example, it's entirely possible that a "global" value cell for "a" 
will be allocated, which is essentially different from the top-level global 
value for "a".  Something like this is possible with "block compilation" in 
Interlisp -- the BLOCK declaration disconnects most of the names appearing 
therein from the outside world, and the compiled image doesn't even have to 
have symbols existing with those names (because the code is compiled in a
fashion similar to the more vanilla, strongly-typed languages,in which "global
variables" are just names for specific runtime memory cells). 

Also, some comments in the recent discussion seem to be misleading as to
how a deep-bound implementation works;  there is no "re-binding" of the
toplevel global value cell, but rather a stackframe which binds a special
variable will have a value cell right in that frame (and will also have the 
name of the variable in the frame, at least implicitly if not directly
present in the frame).  I'm not sure if there is a good reference on deep
binding in general -- the "spaghetti stack" papers are one source -- but
several implementations which I've seen have independently invented similar
sorts of "wheels" for accelerating performance.  [Or, at least they did so 
after a year's experience using the "oval wheels" they started out with!]

Perhaps you, and/or the other people who have volunteered opinions on this 
topic, can comment on whether a GLOBAL declaration, in parallel to the SPECIAL
declaration, would be satisfactory; or whether there is a need for a more 
scheme-like mechanism also.

-- JonL -- 




---------------------------------------------------------------------------
A "white paper" from March 1985
---------------------------------------------------------------------------

    A serious shortfall exists in the defined capabilities for treating
variables either as global or as dynamic, which will badly impact the user
trying to write sensible code runnable both in a deep bound implementation 
and in a shallow bound one.  Common Lisp has gone on record as not being
limited to shallow-bound implementations, and with the appearance of
multi-processor work-stations and "computers", the issue of a deep-bound
implementation of CL is coming to reality.

    This note is rather long, so I've organized the remainder into
four sections, with the the more important items first.
  ** Statement of the Problem
  ** Comparisons with Deep- and Shallow-Bound Interlisps
  ** A Noticeable Semantic Distinction 
  ** Benefits of Distinction


                   Statement of the Problem

    In Commonlisp Lisp, there is no means to specify that a variable's
references are "global" rather than dynamic [of course, this only applies 
to free variable references -- global variables may not be bound].  
Section 5.3.2 of the CL manual recommends 'defvar' as the means to declare 
a variable as special, and (unwisely, I think) suggests this as the means of 
defining "globally defined variables"; defconstant adddresses an issue not
really related to "variables", but rather to named constants (i.e., you cannot
setq a symbol which has been defined by defconstant); defparameter isn't
quite right either, depending on one's implementation (most implementations
I've seen cause defparamater to do a SPECIAL proclaimation on the variable).

         Comparisons with Deep- and Shallow-Bound Interlisps

    Interlisp-10 is a shallow-bound implementation, whereas Interlisp-D and 
Interlisp/VAX are deep bound; so this problem of distinction has already been 
faced in that world.  Since the shortfall is hardly perceptible to the user 
of a shallow-bound implementation, I recommend reading that part of the 
Interlisp reference manual dealing with the differences between the functions 
GETTOPVAL, GETATOMVAL, and EVALV.  [This is found in section 2.4.1 of the 
Oct 1983 version of the IRM, and at the end of section 5.1 of the Oct 1978 
version].   The comments therein about performance implications are not just 
theoretical, but have been observed to account for an order-of-magnitude 
difference between a poorly ported application and a properly ported one.  
Interlisp's EVALV corresponds by definition to CL's 'symbol-value'  
[note well: symbol-value is *not* defined by reference to a "value cell", 
but rather to the dynamic binding environment]; but there is no CL equivalent 
to GETTOPVAL, and GETATOMVAL.


                       A Proffered Solution

    Actually, defvar "proclaims" a variable to be special; to meet the 
problems of this shortfall, there would need to be a declaration of globality
for variables, just as there is a declaration of dynamic scoping for them (see
the "special" section of section 9.2 in the manual).  The observable 
differences between a variable of global scope and one of dynamic, or special,
scope are:
  (1) it is an error to bind a global variable, but setq'ing is ok; and 
  (2) a deep-bound implementation would do a "shallow" value-cell fetch
      for global references, rather than the kind of free-variable lookup 
      it must do for dynamic references.
Additionally, there should be a 'defglobalvar' which proclaims a variable to 
be of global scope, just as defvar proclaims one to be of dynamic scope.


                A Noticeable Semantic Distinction 

    In addition to the perceived performance loss (in the deep-bound case)
of not making accurate global declarations, there is in fact a semantic
difference discernible in both deep and shallow.  
    (setq x "Topval for x")
    (defun lookat () 
      (let ((x "Random dynamic val for x")) 
           (declare (special x)) 
           (list (see-special) (see-global))))
    (defun see-special () (declare (special x)) x)
    (defun see-global () (declare (global x)) x)
When the global declaration is correctly done, the result of (lookat) will
be ("Random dynamic val for x"  "Topval for x") rather than
("Random dynamic val for x"  "Random dynamic val for x")
[Let me forstall any side-tracking discussion about the inadvisibility of 
using a variable both globally and specially in the same module.  This example 
is only for illustrative purposes;  the issue is really the protection of 
one module's usages against those of others, just as a local variable in one 
function needs to be sheilded from the dynamic bindings in another, lexically 
different, function.]


                    Benefits of Distinction

    For the record, I will say that I believe programmers have in general 
overused dynamic variables in the past, ** particularly when attempting to
define global state variables -- I've seen a lot of code that mistakenly 
defines some global variable, which in fact holds something that is process
dependent, or application dependent.   I think this will change in the future,
due to the influence of Scheme and to the insistence by this community for 
correct lexical scoping in Lisp interpreters.  This statement is only a 
generalization, but it comes from observing much code that had to be re-thought
very carefully in the face of a multi-processing Lisp.  One must distinguish 
global state in a machine, 
    such as *modules*  (section 11.8 of CL manual), 
    or, say, *network-routing-table*
from dynamic state
    such as *package* or *read-base*
    or, say, *standard-output* (which will be process-specific, at least)
Properly used, each has their own place, that will become more individually
secure as deep-bound implementations of CL come into being.


-- JonL --