User Tools



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


defsetf 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.)

It must be the case that the function or macro named by access-fn evaluates all of its arguments.

defsetf 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, update-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).

The long form defsetf resembles defmacro. The 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.

The expansion function is defined in the same lexical environment in which the defsetf form appears.

During the evaluation of the forms, the variables in the lambda-list and the store-variables are bound to names of temporary variables, generated as if by gensym or 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 access-fn.

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

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

If 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.


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

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


(defun set-middleguy (x v) (unless (null x) (rplaca (nthcdr (truncate (1- (list-length x)) 2) x) v)) v)


(defsetf middleguy set-middleguy)


(defparameter *a* (list 'a 'b 'c 'd))


(defparameter *b* (list 'x))


(defparameter *c* (list 1 2 3 (list 4 5 6) 7 8 9))


(setf (middleguy *a*) 3)


(setf (middleguy *b*) 7)


(setf (middleguy (middleguy *c*)) 'middleguy-symbol)



(A 3 C D)




(1 2 3 (4 MIDDLEGUY-SYMBOL 6) 7 8 9)

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


;;; TODO this example is terrible, replace it

(defvar *xy* (make-array '(10 10)))


(defun xy (&key ((x x) 0) ((y y) 0)) (aref *xy* x y))


(defun set-xy (new-value &key ((x x) 0) ((y y) 0)) (setf (aref *xy* x y) new-value))


(defsetf xy (&key ((x x) 0) ((y y) 0)) (store) `(set-xy ,store 'x ,x 'y ,y))


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


(setf (xy 'x 1) 1)


(xy 'x 1)


(let ((a 'x) (b 'y)) (setf (xy a 1 b 2) 3) (setf (xy b 5 a 9) 14))


(xy 'y 0 'x 1)


(xy 'x 1 'y 2)


An example involving structures and multiple values in defsetf:

(defstruct point x y z)


(defsetf foo (point &key kind) (x y z) `(ecase ,kind (:2d (setf (values (point-x ,point) (point-y ,point)) (values ,x ,y))) (:3d (setf (values (point-x ,point) (point-y ,point) (point-z ,point)) (values ,x ,y ,z)))))


(defvar *point-1* (make-point))


(defvar *point-2* (make-point))


(setf (foo *point-1* :kind :2d) (values 1 2))

1 2

(setf (foo *point-2* :kind :3d) (values 8 9 10))

8 9 10


#S(POINT :X 1 :Y 2 :Z NIL)


#S(POINT :X 8 :Y 9 :Z 10)

Affected By


Exceptional Situations


See Also


forms 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.

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.