[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
DECLARE SPECIAL Considered Confusing
I do not wish to defend the current choice of declaration-scoping rules
in Common Lisp as the best or only choice, but I do wish to clarify what
the rules are in the language as it is currently defined. It wouldn't
bother me if a future revision of the language adopted simpler rules,
provided they were truly (not just superficially) simpler.
Date: 5 Jul 86 16:54 PDT
From: Pavel.pa@Xerox.COM
I have a question concerning some ambiguity in the description of the
scope of a special declaration inside a LET. Consider this code:
(setq foo 6)
(let ((foo 7))
(let ((foo (1+ foo)))
(declare (special foo))
foo))
Both Symbolics and CLISP return 8 for this, but VAXLISP returns 7.
VAXLISP is correct here. The SPECIAL declaration is pervasive for
references (but non-pervasive for bindings). Because SPECIAL is
pervasive for references it affects the reference to foo inside 1+.
I apologize for the incorrect earlier version of this message that
said that VAXLISP was incorrect here. I didn't mean to add to the
confusion; it must have been a Freudian slip.
Incidentally, the incorrect result returned here by the Symbolics
implementation is a (poorly) documented incompatibility with Common
Lisp that will be fixed at some time in the future.
Clearly, the question is whether or not the special declaration covers
the init-form of the enclosing LET or just the bindings themselves.
According to the ``nonsense'' example in CLtL, page 155, the reference
to ``foo'' in the call to 1+ should be special. The GLS clarification
for page 159, however, seems to support a different philosophy:
``(*) 159 Clarify that in the following example
(defun foo (x)
(declare (inline bar))
(bar x) ; first
(flet ((bar (z) (bar (+ z 1)))) ; second
(bar x)) ; third
(bar x) ; fourth
the first, second and fourth calls to BAR are affected by the INLINE
declaration, but not the third one.''
This seems to support the view that the init-form of a binding is in a
scope outside of that of the binding itself and the body of the LET (or
FLET or ...). I prefer this view.
The scoping of variables (and FLET functions) is different from the
scoping of pervasive declarations. There is no analogy between this
example and your previous one, because the INLINE declaration is not
attached to a binding, but the SPECIAL declaration is attached to a
binding. All that's shown by this clarification is that INLINE, just
like SPECIAL, is shadowed by an occurrence of another binding of the
same name inside its scope.
Incidentally, the bottom of p.154 says that SPECIAL is the only
declaration that falls into both classes, but I think INLINE is really
in the same category. It concerns a particular binding, but can also be
used in the absence of a binding and is pervasive for references, just
like SPECIAL.
I would like to propose the following rule for the scope of declarations
in binding forms:
``A declaration in a binding form (such as LET) affects the body of the
form and the bindings themselves. It does not affect the init-forms for
the bindings; they are in the same scope as the binding form as a
whole.''
This rule has the advantage (over the rule given for the nonsense
example) that the scope of the declarations is the same as the scope of
the bindings. Thus, for the nonsense example:
(defun nonsense (k x z)
(foo z x) ;First call to foo
(let ((j (foo k x)) ;Second call to foo
(x (* k k)))
(declare (inline foo)
(special x z))
(foo x j z))) ;Third call to foo
the inline declaration affects only the third call to foo (not the
second) and only the references to x and z in the third call to foo are
special (not the reference to x in the second call).
There would be only two exceptions to this rule:
-- In a LABELS binding, references in the definitions of the functions
to the very functions being bound would be affected by any declarations
affecting the bindings themselves. This makes sense because of the
recursive quality of the binding.
-- The PROCLAIM function establishes pervasive declarations, covering
all bindings of and references to the symbols named. Such declarations
can, of course, be countermanded by local declarations or later
proclamations.
What about declarations inside the body of DEFUN? With your rules I cannot see
how a declaration could ever affect the default value forms for &optional, &key,
and &aux variables. And what about declarations inside the body of a LET*? The
scoping of ones attached to variables is fairly obvious, but what about ones
not attached to variables?
We went all through this during the design of Common Lisp (I think the discussion
is available online) and the current rules resulted. Given that we must have
DECLARE at all (a point which you should not necessarily be willing to concede),
the current rules seem to work more consistently than the alternatives that were
considered.