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

SETF-FUNCTION-VS-MACRO (Version 2)



I made the edits as previously announced. I added to Benefits the
improved utility of SETF independent of CLOS. I made a grammatical
change somewhere (I'm having trouble finding it), where I added a
semicolon and changed the active element of a sentence. I made several
changes in case. 


!

Issue:         SETF-FUNCTION-VS-MACRO

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
		   (based on discussion among the CLOS working group)
		     Version 2, 26-Oct-87 Masinter, minor mods

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-FUNCTION-VS-MACRO:SETF-FUNCTIONS
is much simpler in the relevant areas.

PROPOSAL (SETF-FUNCTION-VS-MACRO: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 form (setf (-name- ...) ...), when -name- is defined as a function
(rather than a macro) and no setf macro has been defined for -name-,
expands into a call to a setf function.  The name of this setf function
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, so that setf functions can be defined and
manipulated.

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)		;force correct order of evaluation
          (#: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.

The above rules are actually implemented by GET-SETF-METHOD and
GET-SETF-METHOD-MULTIPLE-VALUE, rather than by the SETF macro itself.
Thus GET-SETF-METHOD generates the appropriate five values for a form
that is not a macro-invocation and does not have a defined setf macro.

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.

Examples:

;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))))

;Another example, showing a locally defined setf function
(defun frobulate (mumble)
  (let ((table (mumble-table mumble)))
    (flet ((foo (x)
             (gethash x table))
           ((setf foo) (new x)
             (setf (gethash x table) new)))
      ..
      (foo a)
      ..
      (setf (foo a) b))))

;get-setf-method could implement setf functions by calling
;this function when rules 1-3 do not apply
(defun get-setf-method-for-setf-function (form)
  (let ((new-value (gensym))
	(temp-vars (do ((a (cdr form) (cdr a))
			(v nil (cons (gensym) v)))
		       ((null a) v))))
    (values temp-vars (cdr form) (list new-value)
	    `(funcall #'(setf ,(car form))
		      ,new-value ,@temp-vars)
	    `(,(car form) ,@temp-vars))))

RATIONALE:

By making the names and arguments of setting functions explicit, CLOS is
considerably simplified.  In addition, this can supersede any proposals
to introduce a lexically local form of defsetf; lexically local setf
functions serve the same needs.

Current code that resembles

 (defsetf foo |setf FOO|)
 (defun foo (x) ..)
 (defun |setf FOO| (x new) ..)

or

 (defsetf foo internal-foo-setter)
 (defun foo (x) ..)
 (defun internal-foo-setter (x new) ..)

can be, but is not required to be, replaced with the following code

 (defun foo (x) ..)
 (defun (setf foo) (new x) ..)

An advantage of this is that several disparate styles of using
DEFSETF can be replaced with a single common style of using
setf functions, making programs more standardized and readable.

CURRENT PRACTICE:

A few Common Lisp implementations already have a similar feature,
in that they allow setting functions named (SETF reader).  We 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. 
Improve usability of SETF.

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, if setf
functions are stressed over setf macros, SETF will be much easier to
teach.

DISCUSSION:

Note that in Common Lisp, setf macro expansion 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.

There was some concern about introducing the notion that the name of the
setf-function associated with FOO should be a list, (SETF FOO).  This is
a considerable extension to the idea of a "function name", at least for
standard Common Lisp implementations that do not implement Lisp machine
style function-specs.

However, the CLOS unsuccessfully tried a number of alternatives.
Fundamentally the problem is that there has to be a name that the user
uses to define the thing and to talk about it.  Trying to hide the name
just means you use a more obscure name, like an alternate syntax for
DEFUN or DEFUN-SETF. Another reason for making the name explicit is to
allow one to use FLET for the setf function -- something which would be
difficult if there is not a name-like entity that can be bound.  


This proposal is not incompatible with other extensions to function
specifications present in some implementations. 


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:

a) Lexically local setf macros, that is, a cross between DEFSETF and
   MACROLET.  This does not appear to be logically necessary.  Would all
   three ways of defining lexically global setf macros need local
   counterparts?
  
b) Define the meaning of defmacro or macrolet of (setf foo)?
   This would be a fourth way to define a setf macro.
  
c)  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.
  
d)  Introduce a new name for SYMBOL-FUNCTION, since it accepts
    non-symbols now. 

e)  Should one allow these extended function names in the car-position
of
	an expression to be evaluated? The extra complexity didn't seem
     justified, instead, an explicit FUNCALL is required.