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

Another omission: global lexicals



    Date: Sun, 15 Dec 1985  14:53 EST
    From: Rob MacLachlan <RAM at C.CS.CMU.EDU>

        It is also worth noting that it should be trivial to add global
    lexicals to any implementation.  The value is kept in the global value
    cell, just as for a special; the only difference is that there is no
    special annotation.

I interpret your suggestion as saying that DEFGLOBAL means the same
thing as SETQ, except that it lets compilers know that you really do
want a global lexical variable, so they shouldn't generate warning
messages that they might otherwise be inclined to generate ("FOO
declared special").  If you dynamically bind one, everyone sees the
dynamic binding.

I agree that this would be nice.  You have to make sure you know how
implementations which use deep binding for special variables are going
to deal with this; my interpretation of your proposal makes different
implementation demands from Weinreb's interpretation.  However, I think
deep-bound Common Lisps can simply cause references to DEFGLOBAL'ed
variables to always go through its top-level value cell (presumably one
still exists, even if the variable has been dynamically bound).
SYMBOL-VALUE would still always get the special binding.

I think one could go either as you suggest or as Weinreb suggests on
this.  My reading of your proposal seems to be a less radical departure
from common lisp.  But it's important to keep in mind the distinction
and consider the ramifications and possible generalizations of any
proposal.

I'm also frustrated by the absence of global lexical variables.  There's
no way you can PREVENT a LET or LAMBDA from doing dynamic binding if the
variable obtained its top-level binding with DEFVAR.  To make matters
worse, some implementations (viz. the one I use) even discourage you
from doing top-level references and SETQ's on variables which haven't
been declared or proclaimed SPECIAL - you get "FOO declared special"
messages (fortunately it does a DECLARE and not a PROCLAIM!).

I asked the list a while ago if anyone could remember why there was no
UNSPECIAL declaration in the language, and I didn't get any answer.  If
UNSPECIAL existed, you could redefine LET, LAMBDA, etc. to put implicit
DECLARE UNSPECIAL's in their bodies, and get the effect of global
lexicals just by using DEFVAR.  The only reason I can think of to
exclude UNSPECIAL is to avoid putting constraints on deep-bound or
hybrid implementations.

The solution I ended up using is this: I use SETQ, never DEFVAR to
introduce a top-level binding; I always write

  (locally (declare (special x)) x)

to reference the global "x" (to inhibit the compiler's automatic DECLARE
SPECIAL and its annoying warning message); I use

  (let ((temp (compute-value ...)))
    (setq x temp))

to make an assignment (for the same reasons - and macroized, of course);
and I never use implicit (proclaimed) SPECIAL declarations - if I want
to do a special binding I use a DECLARE.

Of course, my code will very likely stop working if I ever run it in a
deep-bound implementation.

In short: DEFGLOBAL is an important addition to the language, and very
easy to implement.  (It will probably wreak havoc on the manual - how
many places implicitly assume that global variables are always special?)

-----
[Aside: MIT Scheme and T have a generalization of this which allows ANY
lexical variable to be dynamically bound.  As in your proposal,
top-level lexicals and top-level specials live in the same place.
Specials are lexically shadowed by lexical bindings of the same name,
but any lexical variable, not just a top-level lexical variable, can be
dynamically bound.  Dynamicness is not a property of variables (names),
but of binding.

I would expect people who haven't thought about this before to be
confused.  I certainly was when I first saw it.  Here is an example.

(setq y 'top)

(defun foo1 ()
  (let ((y 'lexical))
    (bar #'(lambda (f)
	     (fluid-let ((y 'fluid))
	       (funcall f)))		;scheme dynamic binding
	 #'(lambda () y))))

(defun foo2 ()
  (let ((y 'lexical))
    (bar #'(lambda (f)
	     (let ((y 'fluid))
	       (declare (special y))	;CL dynamic binding, for comparison
	       (funcall f)))
	 #'(lambda () y))))

(defun bar (g h)
  (funcall g #'(lambda () (list (funcall h) y))))

(foo1)  =>  (FLUID TOP)
(foo2)  =>  (LEXICAL FLUID)  ;[probably, at least in shallow implementations]

The semantics of (fluid-let ((x value)) body) is similar to

(let ((temp x))
  (setq x value)
  (unwind-protect body
		  (setq x temp)))

except of course that it interacts properly with context switches.  It
turns out this facility is moderately useful.  And it works in either
shallow-bound implementations or deep-bound ones (both MIT Scheme and T
use shallow binding right now, but T is changing to deep binding).

I think this is subsumed by Zetalisp's LETF, for those of you familiar
with that.]

- Jonathan