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

Issue: PROCLAIM-LEXICAL (Version 8)



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.