[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
- To: email@example.com
- Subject: "lexical" globals?
- From: firstname.lastname@example.org (Jon L White)
- Date: Tue, 17 Dec 85 10:50:24 pst
- Cc: navajo!common-lisp@su-ai
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,
(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
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 --