[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Issue: PROCLAIM-LEXICAL (Version 8)
- To: Masinter.PA@Xerox.COM
- Subject: Issue: PROCLAIM-LEXICAL (Version 8)
- From: Kent M Pitman <KMP@STONY-BROOK.SCRC.Symbolics.COM>
- Date: Thu, 6 Oct 88 05:05 EDT
- Cc: CL-Cleanup@SAIL.Stanford.EDU
Per your request, here's a new version merging recent discussion.
I don't think this is in a form where it's ready to be voted on, but
maybe it's suitable for X3J13 to be reading over and thinking about
while we work on banging out the details that are up in the air.
-----
Issue: PROCLAIM-LEXICAL
References: variables (p55), scope/extent (p37), global variables (p68),
declaration specifiers (p157)
Category: CLARIFICATION/ADDITION
Edit history: Version 2 by Rees 28-Apr-87
Version 3 by Moon 16-May-87
Version 4 by Masinter 27-Oct-87
Version 5 by Masinter 14-Nov-87
Version 6 by Pitman 15-Sep-88
(major revision, for review by Jonathan Rees and Jeff Dalton)
Version 7 by Pitman 24-Sep-88
(minor revisions based on comments from Rees and Dalton)
Version 8 by Pitman 06-Oct-88 (merge recent discussion)
Status: For Internal Discussion
Problem Description:
Although local variables in Common Lisp may be `special' or `lexical,'
global variables (with the exception of named constants) may currently
only be `special.'
The Scheme language permits free variable references to refer to global
bindings. Their experience suggests that such usage would be useful to
the Common Lisp community. The absence of such a facility in Common Lisp
is a barrier both culturally (to the sharing of ideas) and technically
(to the sharing of code).
SPECIAL proclamations are uncontrollably pervasive. There is no way
to locally override or globally undo a SPECIAL proclamation.
Background/Analysis:
Variable evaluation may be viewed in Common Lisp as a search through
a set of environments to find a binding, and then the dereferencing of
that binding. The environments with which Common Lisp deals are
Lexical (L), Dynamic (D), and Global (G).
A SPECIAL declaration for a variable amounts to a request that the
variable be resolved by searching first the Dynamic and then the Global
environment (DG).
As currently described in CLtL, lexical variable reference searches
only the Lexical environment (L).
Because undeclared free variables in the interpreter are implicitly
declared SPECIAL by most (perhaps all) implementations, this amounts
to a search of Lexical, Dynamic, and Global (LDG). However, the
accompanying warnings in many implementations make it clear that this
behavior is not intended to be taken seriously.
Constants are looked up solely in the Global environment (G). They
have other properties as well, of course.
In the Scheme language, the default lookup is first Lexical, then
Global (LG). Providing compatibility for Scheme code is, and more
generally for a Scheme working style is therefore difficult because
Common Lisp does not provide the LG search style.
The issue of whether a variable can be assigned is orthogonal.
The issue of whether a variable can be bound and, if it can be, which
environment is used for the new binding is orthogonal.
Proposal (PROCLAIM-LEXICAL:LG):
Provide a new declaration (and proclamation) called LEXICAL which does
LG lookup. That is, variables declared LEXICAL would be looked up first
in the lexical environment (L) and then in the global environment (G)
if not found in the lexical.
Clarify that dynamic binding does DG lookup. That is, variables
declared SPECIAL would be looked up first in the dynamic
environment (D) and then in the global environment (G) if not found
in the lexical. Further clarify that SYMBOL-VALUE does DG lookup.
Define that a dynamic binding of a variable creates a new binding
in the dynamic environment (D) leaving the global environment (G)
unaffected.
Define that a lexical binding of a variable creates a new binding
in the lexical environment (L), leaving the global environment (G)
unaffected.
Note that an assignment to a variable which is bound in the global
environment (G) will affect lexical (LG) lookups for which there is
no lexical (L) binding and dynamic (DG) lookups for which there is
no dynamic (D) binding.
Note that these restrictions describe an abstract model, not a
concrete implementation. An implementation may still choose to
implement dynamic binding as either deep or shallow, but some
searching may be necessary to find the global cell in shallow bound
implementations [unless dynamic binding has been forbidden for
that variable].
Like SPECIAL declarations (and unlike type declarations),
compilers and interpreters would be required to notice and
respect this declaration.
Test Case:
#1: (proclaim '(lexical x))
(setq x 1)
(defun f (fn) (list x (funcall fn)))
(defun g (fn)
(let ((x 2))
(declare (special x))
(funcall fn #'(lambda () x))))
(g #'f) => (1 2)
#2: ; Warning: It is unlikely that any serious program would
; be written in so obscure a manner as this example.
; This just tests the fringe cases.
(proclaim '(lexical x))
(proclaim '(special y))
(setq x 1 y 2)
(defun tst ()
(let ((x 3) (y 4))
(locally (declare (special x) (lexical y))
(list x y
(funcall (let ((x 5) (y 6))
#'(lambda () (list x y))))))))
(tst) => (1 2 (5 4))
If the results of this example confuse you, keep in mind
that the results of code like this would be somewhat
confusing no matter what the chosen semantics because the
code itself is far from perspicuous.
An explanation of this behavior, which some people find less
than intuitive because of the bizarre choice of constructs:
X gets bound lexically to 3 because X is [pervasively]
proclaimed LEXICAL.
Y gets bound specially to 4 because Y is [pervasively]
proclaimed SPECIAL.
Reference style for name X is changed to SPECIAL, making
lexical X=3 invisible.
Reference style for name Y is changed to LEXICAL, making
dynamic Y=4 invisible.
Global X=1 and global Y=2 are first two elements of list.
X gets bound lexically to 5 because X is [pervasively]
proclaimed LEXICAL.
Y gets bound specially to 6 because Y is [pervasively]
proclaimed SPECIAL.
Closure is returned, capturing [lexical] X=5 but not
[special] Y=6.
Dynamic binding of Y to 6 disappears, dynamic binding
of Y to 4 reverts.
Closure is funcalled, returning captured X=5 and dynamically
active Y=4 in a list which becomes third list element.
Rationale:
This mechanism provides a simple and straightforward answer to
the problems stated above.
Current Practice:
Probably no one implements this.
Cost to Implementors:
A fair amount compiler work would probably be needed. Some compilers
may have hooks for most of this already laying around, but some may not.
Note well that this proposal does not require separate global lexical
and dynamic cells, so the data storage layout of Lisp need not change.
Moon says...
I have now thought of an efficient way to do this on Lisp machines,
using invisible pointers, and another efficient way to do it on
stock hardware, using one extra instruction on every global
reference of one or the other sort, plus a few extra instructions
in SPECIAL binding and unbinding. Given that, I no longer object
to the proposal as unimplementable.
It doesn't just require a few compiler changes, it requires some
reimplementation of the representation of global variables, with
concomitant changes to the compiler, the loader, the interpreter,
and probably the debugger. Every symbol now potentially has two
values accessible from the interpreter (the current SPECIAL and
the global LEXICAL) and you need the corresponding new data
structure to keep track of that.
Rees suggests...
In shallow-bound implementations, implementors may have to add a
small run-time routine that searches the dynamic saved-binding
stack to look for the global value in the case where the variable
has been dynamically bound. One might want a bit (or a count)
somewhere (perhaps in the symbol itself) to speed up the common
case of access to a global binding of a variable that hasn't been
dynamically bound; without some kind of optimization, you have to
search the whole saved-binding stack on every reference to a
free [lexical] variable.
While naively you might think you'd incur the cost of clearing the
valid bit on every dynamic binding (not acceptable), in actuality
the bit is a static property of programs (PROGV excepted). So the
only places you ever need to clear FOO's valid bit are in PROGV,
in the interpreter, and when FASLOADing code that contains a compiled
dynamic binding of FOO.
Cost to Users:
For the most part, this change is upward compatible.
Some code-walking tools would have to change.
Cost of Non-Adoption:
It would continue to be difficult to share code with Scheme.
New CL users coming from the Scheme community would be confused by
their sometimes inability to map what they know about variable binding
into the CL model of variable binding.
Some interesting native CL applications would be impossible to write
in a syntactically convenient style.
Benefits:
Enhanced flexibility of expression.
Rationalization of the semantics of dynamic variables.
Aesthetics:
Improved appeal to a certain sector of the programming community.
Discussion:
Rees points that it is an oversimplification to describe Scheme's
binding simply as LG since they have no Dynamic environment and
there is no way to distinguish LG and LDG. However, the reasons he
prefers LG are:
1. It's nice for readability and understandability to have a
declaration which tells you that a variable will not be
dynamically bound.
2. It's nice for performance in deep-bound implementations to have a
declaration that says that no search will be needed.
Of course, he notes, there could be a counter-argument to item 2
(in favor of LDG) in order to prefer shallow bound implementations,
but that still would not defeat the argument in item 1. Rees believes
that LG is slightly preferrable, but that LDG would be essentially
adequate for most of his needs.
Pitman supports PROCLAIM-LEXICAL:LG and believes that giving LDG the
name LEXICAL would be a serious mistake, leaving open the door for
program bugs due to accidental binding of variables presumed by the
programmer not to be bound. If someone (Moon?) seriously wanted LDG
type variables in addition to LG variables (under a name other than
LEXICAL), Pitman would not object.
Dalton expressed support for PROCLAIM-LEXICAL:LG (Version 6).
He observes that another reason for opposing LDG is that it suggests
the possibility that someone might want DLG. LG is simpler and still
accomplishes the stated purpose. He adds ``I would like to be able
to explain the global environment as a sort of giant, extensible
LET abound everything. This proposal seems to get fairly close.''
It would be possible to submit a proposal for a GLOBAL (G) declaration
under separate cover if anyone (Xerox?) was interested. Pitman thinks
this would be an interesting idea. Dalton points out, however, that
already with this proposal there is enough power to at least deal with
globals -- albeit circuitously. For example, to reference a global
variable X, one could write subroutines such as:
(defun global-x () (declare (lexical x)) x)
(defun set-global-x (value) (declare (lexical x)) (setq x value))
Eg, consider:
(defun f (x) (+ (global-x) x))
In principle, we could imaging saying that free variables should be
lexical by default, but that would only reduce error checking to no
good end. To be really useful, this proposal will need to be followed
by a proposal for primitives analogous to DEFVAR and/or DEFPARAMETER
but for lexical variables. However, since arguments over syntax are
likely to have plenty of issues of their own, we've separated this
proposal for primitive functionality from issues of syntax which
can be dealt with separately once this is passed.
Moon expressed concerns about the efficiency issues but after
thinking about it for a while convinced himself that this is
efficiently implementable both on stock and special purpose hardware.
JonL expressed concerns about the last-minute nature of this change,
which he sees as untested.
Dalton suggests that an alternative solution to the speed issue
might be possible to obtain by restricting a particular variable to
be either LEXICAL or SPECIAL but not both.
Dalton points that even if people don't like the details here, there
must be a better fallback solution than "do nothing". Pitman agrees
heartily.