[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
fixing our problems with setf
After thinking it over, I think Gregor's proposal to simplify the
way setf-functions work is reasonable. There will be some adoption
cost for us, because the order of arguments to a setf function is
being changed from what we agreed on before, but I think we can
come up with a compatibility kludge to take care of that. Of course,
Gregor's proposal doesn't really eliminate the scoping issues, not
only because we cannot get rid of defsetf and define-setf-method,
but also because of the already defined interaction of setf with
macros. However, I think the scoping rules are quite straightforward.
Here is a revised version of what I mailed out on September 28,
modified and simplified to reflect what Gregor proposed:
The goal is to unify the handling of "setf functions" with the handling
of regular functions, so we don't need a proliferation of -setf versions
of defmethod, defgeneric, ensure-generic-function, fboundp, generic-labels,
with-added-methods, etc. The major issue is that Common Lisp has not seen
a need to do this already, so the onus falls on CLOS. In addition, we
ran into difficulty with confusion between the idea of associating a
function name with a function object (in our case, a generic function
object), and the idea of associating a way to setf a function with that
function. This proposal clarifies the situation.
Add to Common Lisp the same concept of "setf functions" that we are
already introducing in CLOS. Right now, Common Lisp only has "setf
macros", which are defined by define-setf-method and both forms of
defsetf. I draw the distinction because 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,
while a "setf function" is something that is called to perform directly
the effect of an invocation of setf.
As with regular functions, associated with any given name you can have a
setf function or a setf macro, but not both. This means that one does
not define a setf function (with defmethod or defgeneric) and also call
defsetf. The mere act of defining the setf function is enough to tell
setf what to do. In fact setf only needs for the setf function to be
defined at run time, not at compile time.
Since setf functions are in a separate, but parallel, namespace from
regular functions, we need a way to name them. The simplest way is to
allow a list (setf -name-) to be used as the name of the setf function
that is called to perform the effect of (setf (-name- ...) ...). The
following functions, macros, and special forms defined in CLtL need to
be enhanced to accept such lists where they now accept symbols as
symbol-function and setf of symbol-function
and the declarations ftype, function, inline, and notinline
This makes the name of symbol-function a bit obsolete, but I do not
propose to introduce a new function to replace it. The discrepancy is
not that important.
The following functions, macros, and special forms defined in CLOS need
to be enhanced in the same way:
defmethod-setf and defgeneric-setf need to be removed.
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
Note that I do not propose to introduce lexically local setf macros,
that is, a cross between defsetf and macrolet. This does not appear to
be logically necessary. If someone else wants this, it would certainly
not be hard to do. The main issue is whether all three ways of defining
lexically global setf macros need local counterparts. A secondary issue
is whether to define the meaning of defmacro or macrolet of (setf foo).
I also do not now propose to clarify 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.
These issues logically belong to Common Lisp, not to CLOS.
Contrary to what we already decided about the lambda-list of a
setf-function, the new value to be stored will be passed as the first
argument. Thus, #'(setf foo) takes one more required argument than
#'foo, the first required argument is the new value to be stored, and
the remaining arguments are the same as #'foo's arguments.
The function-defining macros defun, flet, labels, defgeneric, defmethod,
and the :method option to defgeneric, generic-flet, generic-labels, and
with-added-methods will not have a special syntax with two lambda-lists
when defining a setf function, contrary to what we decided before. The
programmer writing a setf-function must know to insert the new-value
parameter at the front of the lambda-list.
The remaining issue is a scoping issue. We have introduced lexically
local setf functions, where before Common Lisp only had lexically global
setf macros. Thus the namespace of setf operators has been extended to
have a lexical component, just like the namespace of regular operators.
(Recall that "operator" means the union of functions, macros, and
special forms). Regular functions and setf functions naturally come in
pairs, but since they are defined separately we have to specify what
happens in various cases where only one is defined at a given lexical
contour. The following rules for the behavior of SETF suffice; 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
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)
(funcall #'(setf foo) #:temp-2 #:temp-1))
Note that rule 4 is independent of the scope of the function name
(setf foo) and does not care 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 the
rules more complicated.
Example (for Patrick):
(defmethod (setf subseq)
((new-value vector) (sequence vector) 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 (aref sequence i) (aref new-value j))))
If this meets with general approval I will recast this for the Cleanup