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

fixing our problems with setf



Here is the proposal I will send to the cleanup committee Wednesday
evening, after incorporating any comments you have to offer:

Issue:         SETF-CLOS

References:    setf rules for what -place- can be (pp.94-7)
	       compile function (p.438)
	       defun macro (p.57)
	       disassemble function (p.439)
	       documentation function (p.440)
	       fboundp function (p.90)
	       flet special form (p.113)
	       fmakunbound function (p.92)
	       ftype declaration (p.158)
	       function special form (p.87)
	       function declaration (p.159)
	       inline declaration (p.159)
	       notinline declaration (p.159)
	       labels special form (p.113)
	       symbol-function and setf of symbol-function (p.90)
	       trace macro (p.440)
	       untrace macro (p.440)

Category:      ADDITION

Edit history:  Version 1, 13-Oct-87 Moon

Problem description:

The Common Lisp Object System needs a well-defined way to relate the
name and arguments of a setting function to those of a reading function,
because both functions can be generic and can have user-defined methods.
We tried to hide the name and arguments of the setting function with
macrology, but the complexity got out of hand.  It seems better to make
this information explicit; the version of the CLOS specification that
assumes the adoption of proposal SETF-CLOS:SETF-FUNCTIONS is much
simpler in the relevant areas.

Proposal (SETF-CLOS:SETF-FUNCTIONS): 

Add to Common Lisp the concept of "setf functions".  Right now, Common
Lisp only has "setf macros", which are defined by define-setf-method and
both forms of defsetf.  Terminology:
  - a "setf macro" is something that produces code (or other
    specifications, as in define-setf-method) which, when evaluated,
    will perform the effect of an invocation of setf.
  - a "setf function" is something that is called to perform
    directly the effect of an invocation of setf.

The name of the setf function that is called to perform the effect of
(setf (-name- ...) ...) is a list (setf -name-), where -name- is a
symbol.  The functions, macros, and special forms defined in CLtL and
listed in the References section above need to be enhanced to accept
such lists in addition to symbols as function names.

A setf function receives the new value to be stored as its first
argument.  Thus, #'(setf foo) should have one more required parameter
than #'foo, the first required parameter is the new value to be stored,
and the remaining parameters should be the same as #'foo's parameters.

A setf function must return its first argument, since setf is defined
to return the new value.

A definition of a setf function can be lexically local, like a
definition of a reading function.  The following rules specify the
behavior of SETF; note that these rules are ordered and the first rule
to apply supersedes any later rules.  These rules are a consistent
extension of the current behavior of Common Lisp and the Cleanup
committee's resolution of issue GET-SETF-METHOD-ENVIRONMENT.  Only
rule 4 is new with this proposal.

Rules for the macroexpansion of (setf (foo x) y):

(1) If the function-name foo refers to the global function definition,
rather than a locally defined function or macro, and if there is a
setf macro defined for foo, use the setf macro to compute the expansion.

(2) If the function-name foo is defined as a macro in the current scope,
use macroexpand-1 to expand (foo x) and try again.

(3) If the function-name foo is defined as a special form in the current
scope, signal an error.

(4) Expand into the equivalent of
    (let ((#:temp-1 x)
	  (#:temp-2 y))
      (funcall #'(setf foo) #:temp-2 #:temp-1))

Note that rule 4 is independent of the scope of the function name
(setf foo).  It does not matter if that scope is different from the
scope of the function name foo.  This allows some nonsensical programs
to be written, but does not seem harmful enough to justify making more
complicated rules to compare the scopes of the two function definitions.

Normally one does not define both a setf function and a setf macro
for the same reading function.

Normally one defines a local reading function and a local setf function
together in a single FLET or LABELS.

In the absence of any setf macro definition, SETF of a function expands
into a call to the setf function.  This means that the setf function
only needs to be defined at run time, not compile time.

Test Case:  (really more of an example than a test case)

;If setf of subseq was not already built into Common Lisp,
;it could have been defined like this
(defun (setf subseq) (new-value sequence start &optional end)
  (unless end (setq end (length sequence)))
  (setq end (min end (+ start (length new-value))))
  (do ((i start (1+ i))
       (j 0 (1+ j)))
      ((= i end) new-value)
    (setf (elt sequence i) (elt new-value j))))

Rationale:

By making the names and arguments of setting functions explicit, CLOS is
considerably simplified.

Current practice:

A few Common Lisp implementations already have a similar feature,
in that they have setting functions named (SETF reader).  I don't
know of any implementation that has precisely the proposed feature.

Adoption Cost:

The main cost is generalization of a few functions to accept lists
beginning with SETF where they now accept only symbols.  Implementations
must add a data structure to store the function definition of a setf
function, however, this can trivially be done with property lists or
generated symbols.

The cost of making the SETF macro expand into a call to a setf function,
when it does not find a setf macro or a regular macro to expand, is
negligible.

This will be an incompatible change for Symbolics, since it already has
setf functions but they do not take the same arguments as proposed here.
However, the change is considered worthwhile.

Cost of non-adoption:

Non-adoption of this proposal would be a significant roadblock to the
Common Lisp Object System.  Some major rethinking of CLOS would be
required.

Benefits:

Allow CLOS to be defined without out-of-hand complexity.

Conversion Cost:

None, this is an upward-compatible change.

Esthetics:

SETF would be more esthetic, but less powerful, if it had only the
proposed setf functions and did not have setf macros.  Such a major
incompatible change is of course out of the question, however by
stressing setf functions SETF could become easier to teach.

Discussion:

Note that in Common Lisp, setf macroexpansion is an operation on
function names, not on functions.  It differs from some dialects of
Scheme, such as T, in this respect.  This proposal does not attempt to
change that.

The following related features were considered but are specifically
not being proposed at this time, since they are unnecessary for CLOS
and appear not to improve the simplicity and esthetics of the language:

  Lexically local setf macros, that is, a cross between defsetf and
  macrolet.  This does not appear to be logically necessary.  Do all three
  ways of defining lexically global setf macros need local counterparts?
  
  Should we define the meaning of defmacro or macrolet of (setf foo)?
  This would be a fourth way to define a setf macro.
  
  Should we enhance the definition of global setf macros, for example to
  say that (macro-function '(setf foo)) returns an expander function that
  takes two arguments and returns five values?
  
  Should we introduce a new name for symbol-function, since it accepts
  non-symbols now?