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

fixing our problems with setf

As I believe I made clear before, I believe Common Lisp would be a
better language if setf facility it provided was a lot simpler.  This
message starts by making more clear what it is I think would have been a
better original design, it then goes on to propose a clean up of setf
which has many of the same nice properties but is compatible with the
current situation.

In my 'improved original design' the setf mechanism would just be an
abbreviation for calling a function whose name was (SETF -symbol-).
There would be no defsetf or any other mechanism for changing the way
the setf macro would expand under certain circumstances.  Here is an
example of how this would work:

(setq mex (macroexpand '(setf (foo 1 2) 3)))
(LET ((#:G0 1)
      (#:G1 2)
      (#:G2 3))
  (FUNCALL #'(SETF FOO) #:G2 #:G0 #:G1))

;;; at this point, assuming there was no previous definition for
;;; (setf foo) attempting to evaluate the form bound to MEX would
;;; signal an error.

(eval mex)
>Error: undefined function (SETF FOO)...

;;; but if we define a function (SETF FOO) then eval of mex would work
;;; as expected

(defun (setf foo) (new-value indicator) ...)

What are the features of this mechanism:

  The macroexpansion of setf does not depend on any defining form,
lexical environment, compile environment or anything.  Because it is not
possible to do a 'top-level' definition for setf expansion, there are no
problems with wanting to do a lexical binding of a rule for setf

  Also because the expansion of setf does not depend on the argument
list of FOO or (SETF FOO), there can't be any problems with having to
re-expand (recompile) code after the defun for foo or (setf foo)
changes.  This rule for setf always puts the new value argument as the
first of the other arguments, this rule always works since it doesn't
depend on the definition of FOO or (SETF FOO).

Of course, proposing to Common Lisp that they completely do away with
defsetf is probably too radical a proposal.  But I believe there is a
useful intermediate ground which could help us all.

1) Change the behavior of setf so that in the absence of any
   special information provided by defsetf it expands as shown

2) Add the setf function spec to the language to support this.
   But note that the forms like defun and defmethod would only
   have one lambda-list.  The programmer would know that the
   function/method will be receiving the new-value as its first

3) Document very clearly that this is the suggested way of using
   setf and that the only real reason to continue using the old
   form of setf is to do things like LDB or other fundamentally
   macro-ish things.

I believe this proposal solves many of the problems previously raised in
a clean simple way:

    Date: Tue, 22 Sep 87 09:36:10 CDT
    From: Patrick H Dussud <DUSSUD%Jenner@ti-csl.CSNET>

    The problem is that we can't always specialize on the new-value argument
    using the short defsetf form.  The programmer will have to write a hocky
    defsetf form to get around the problem of having optional arguments
    (e.g.  implementing the setf form of SUBSEQ as a generic function).  We
    lose the COMBINE-LAMBDA-LISTS abstraction.

In this scheme we can always specialize on the new value form since the
programmer can count on it coming in as the first argument to the setf

    Date: Mon, 28 Sep 87 16:55 EDT
    From: David A. Moon <Moon@STONY-BROOK.SCRC.Symbolics.COM>

    The next issue is what to do about the lambda-list of a setf-function.
    We already agreed on what is the lambda-list in the expansion of
    (defun (setf foo) ...) into 
    (setf (symbol-function '(setf foo)) #'(lambda ...)).  Specifically,
    #'(setf foo) takes one more required argument than #'foo, and the last
    required argument is the new value to be stored.

    The issue is whether certain function-defining macros should have a
    special syntax with two lambda-lists when defining a setf function, or
    should use their normal syntax.  The macros in question are defun, flet,
    labels, defgeneric, defmethod, and the :method option to defgeneric,
    generic-flet, generic-labels, and with-added-methods.  In favor of two
    lambda-lists is that it's easier for the programmer to see which
    parameter is bound to the new value to be stored.  In favor of one
    lambda-list is syntactic consistency between setf functions and regular
    functions.  At the meeting two weeks ago, we favored two lambda-lists,
    but I now believe that that was a mistake.  The rule that the new value
    is the last required argument should not be at all difficult for
    programmers to understand.  Syntactic consistency is important.

I believe making it be the first argument is better because the rule you
propose loses in the following scenario.

(defun foo (a b) ..)
(defun (setf foo) (a b new-value) ..)

(defun code-calling-setf-of-foo ()
  (setf (foo a b) c))

Now at this point the programmer realizes that they would rather have
the second argument to FOO be optional.  But they are screwed because
they have to go back and recompile all the code that uses setf of foo.

    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

Scoping is not a problem in my proposal provided the programmer never
uses defsetf.  Because setf always expands the same way, all the
programmer needs to do is provide lexical definitions for the actual
setf function (SETF FOO).