Macro defsetf

Syntax:

The “short form”:

defsetf access-fn update-fn [documentation] access-fn

The “long form”:

defsetf access-fn lambda-list ({store-variable}*) ⟦{declaration}* | documentation⟧ {form}* access-fn

Arguments and Values:

!!! Barmar: Does the function or macro needs to already be defined?access-fn—a symbol which names a function or a macro.

update-fn—a symbol naming a function or macro.

lambda-list—a defsetf lambda list.

store-variable—a symbol (a variable name).

declaration—a declare expression; not evaluated.

documentation—a string; not evaluated.

form—a form.

Description:

7.2.0 46defsetf defines how to setf a place of the form (access-fn ...) for relatively simple cases. (See define-setf-expander for more general access to this facility.) These next two paragraphs apply only to the short form. This information is presented more succinctly below anyway. --sjl 5 Mar 92 When \macref{setf} is given a \term{place} that is specified in terms of \param{access-fn} and a new value for the \term{place}, it is expanded into a call on \param{update-fn}. The arguments of \param{access-fn} and the new value are passed to \param{update-fn}, and \param{update-fn} is invoked to modify the value of the \term{place}. The \term{function} or \term{macro} named by \param{access-fn} evaluates all of its arguments. Barmar: Why? Barmar (later): Answer is CLtL2, p138, para 2. \param{Update-fn} must take one more argument than \param{access-fn}. This last argument corresponds to the new value that is to be assigned to the \term{place}. \param{Update-fn} must return the new value as its result.It must be the case that the function or macro named by access-fn evaluates all of its arguments.

7.2.0 49defsetf may take one of two forms, called the “short form” and the “long form,” which are distinguished by the type of the second argument.

When the short form is used, The simple form \code (defsetf access-fn update-fn \lbracket\ doc-string\rbracket) \endcodeupdate-fn must name a function (or macro) that takes one more argument than access-fn takes. When setf is given a place that is a call on access-fn, it expands into a call on update-fn that is given all the arguments to access-fn and also, as its last argument, the new value (which must be returned by update-fn as its value).

7.2.0 51The long form defsetf looks like \code (defsetf access-fn lambda-list (store-variable) . body) \endcode andresembles defmacro. 7.2.0 52The lambda-list describes the arguments of access-fn. The store-variables describe the value or values to be stored into the place. The body must compute the expansion of a setf of a call on access-fn. Reworded garbled text. --sjl 7 Mar 92 When the expansion function is called, the \param{forms} are evaluated in the lexical environment in which the \macref{defsetf} \term{form} was evaluated.The expansion function is defined in the same lexical environment in which the defsetf form appears.

7.2.0 54During the evaluation of the forms, the variables in the lambda-list and the Ikky. --sjl 5 Mar 92 \param{store-variable} \issue{SETF-MULTIPLE-STORE-VARIABLES:ALLOW} or \param{store-variables} \endissue{SETF-MULTIPLE-STORE-VARIABLES:ALLOW}store-variables are bound to names of temporary variables, generated as if by gensym !!! Moon doesn't like this. Neither do I. He's sent mail about this. -kmp 13-Feb-92or gentemp, that will be bound by the expansion of setf to the values of those subforms. This binding permits the forms to be written without regard for order-of-evaluation issues. defsetf arranges for the temporary variables to be optimized out of the final result in cases where that is possible.

The body code in defsetf is implicitly enclosed in a block whose name is the same as the accessor.access-fn

7.2.0 47defsetf ensures that subforms of the place are evaluated exactly once.

Documentation is attached to access-fn as a documentation string of kind setf.

added qualification about top-level-ness --sjl 5 Mar 92If a defsetf form appears as a top level form, the compiler must make the setf expander available so that it may be used to expand calls to setf later on in the file. Users must ensure that the forms, if any, can be evaluated at compile time if the access-fn is used in a place later in the same file. The compiler must make these setf expanders available to compile-time calls to get-setf-expansion when its environment argument is a value received as the environment parameter of a macro.

Examples:

The effect of

 (defsetf symbol-value set)
is built into the Common Lisp system. This causes the form (setf (symbol-value foo) fu) to expand into (set foo fu).

7.2.0 50Note that

 (defsetf car rplaca)
would be incorrect because rplaca does not return its last argument.

 (defun middleguy (x) (nth (truncate (1- (list-length x)) 2) x)) → MIDDLEGUY
 (defun set-middleguy (x v)
    (unless (null x)
      (rplaca (nthcdr (truncate (1- (list-length x)) 2) x) v))
    v) → SET-MIDDLEGUY
 (defsetf middleguy set-middleguy) → MIDDLEGUY
 (setq a (list 'a 'b 'c 'd)
       b (list 'x)
       c (list 1 2 3 (list 4 5 6) 7 8 9)) → (1 2 3 (4 5 6) 7 8 9)
 (setf (middleguy a) 3) → 3
 (setf (middleguy b) 7) → 7
 (setf (middleguy (middleguy c)) 'middleguy-symbol) → MIDDLEGUY-SYMBOL
 a → (A 3 C D)
 b → (7)
 c → (1 2 3 (4 MIDDLEGUY-SYMBOL 6) 7 8 9)

7.2.0 56An example of the use of the long form of defsetf:

 (defsetf subseq (sequence start &optional end) (new-sequence)
   `(progn (replace ,sequence ,new-sequence
                    :start1 ,start :end1 ,end)
           ,new-sequence)) → SUBSEQ

People thought this comment was stupid. -kmp 10-Feb-92 Experience with Common Lisp prior to this specification suggests that use of the fully general notation for \term{keyword parameters} in \term{function} definitions is rare, and that its use in \macref{defsetf} \term{forms} is even more rare. In spite of its obscure nature, the following is an illustration of \term{conforming code}:

 (defvar *xy* (make-array '(10 10)))
 (defun xy (&key ((x x) 0) ((y y) 0)) (aref *xy* x y)) → XY
 (defun set-xy (new-value &key ((x x) 0) ((y y) 0))
   (setf (aref *xy* x y) new-value)) → SET-XY
 (defsetf xy (&key ((x x) 0) ((y y) 0)) (store)
   `(set-xy ,store 'x ,x 'y ,y)) → XY
 (get-setf-expansion '(xy a b))
→ (#:t0 #:t1),
   (a b),
   (#:store),
   ((lambda (&key ((x #:x)) ((y #:y))) 
      (set-xy #:store 'x #:x 'y #:y))
    #:t0 #:t1),
   (xy #:t0 #:t1)
 (xy 'x 1) → NIL
 (setf (xy 'x 1) 1) → 1
 (xy 'x 1) → 1
 (let ((a 'x) (b 'y))
   (setf (xy a 1 b 2) 3)
   (setf (xy b 5 a 9) 14))
→ 14
 (xy 'y 0 'x 1) → 1
 (xy 'x 1 'y 2) → 3

Affected By:

None.

Exceptional Situations:

None.

See Also:

documentation, setf, define-setf-expander, get-setf-expansion, Section 5.1 (Generalized Reference), Section 3.4.11 (Syntactic Interaction of Documentation Strings and Declarations)

Notes:

7.2.0 55forms must include provision for returning the correct value (the value or values of store-variable). This is handled by forms rather than by defsetf because in many cases this value can be returned at no extra cost, by calling a function that simultaneously stores into the place and returns the correct value.

A setf of a call on access-fn also evaluates all of access-fn's arguments; it cannot treat any of them specially. !!! Barmar: What if update-fn is a macro? Moon: That technique won't work because DEFSETF is allowed to introduce temporary variable bindings. I guess that ought to be explained in this note.This means that defsetf cannot be used to describe how to store into a generalized reference to a byte, such as (ldb field reference). define-setf-expander is used to handle situations that do not fit the restrictions imposed by defsetf and gives the user additional control.