[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
SETF-FUNCTION-VS-MACRO (Version 1)
- To: CL-Cleanup@sail.stanford.edu
- Subject: SETF-FUNCTION-VS-MACRO (Version 1)
- From: David A. Moon <Moon@STONY-BROOK.SCRC.Symbolics.COM>
- Date: Thu, 15 Oct 87 17:11 EDT
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)
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.
TEST CASE: (really more examples than test cases)
;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 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 rather than setf macros 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?