Skip to main content

get-setf-expansion

get-setf-expansion Function

Syntax:

get-setf-expansion place &optional environment

! vars, vals, store-vars, writer-form, reader-form

Arguments and Values:

place—a place.

environment—an environment object.

vars, vals, store-vars, writer-form, reader-form—a setf expansion.

Description:

Determines five values constituting the setf expansion for place in environment; see Section 5.1.1.2 (Setf Expansions).

If environment is not supplied or nil, the environment is the null lexical environment.

Examples:

(get-setf-expansion ’x) 
*!* NIL, NIL, (#:G0001), (SETQ X #:G0001), X
;;; This macro is like POP
(defmacro xpop (place &environment env)
(multiple-value-bind (dummies vals new setter getter)
(get-setf-expansion place env)
(let\* (,@(mapcar #’list dummies vals) (,(car new) ,getter))
(if (cdr new) (error "Can’t expand this."))
(prog1 (car ,(car new))
(setq ,(car new) (cdr ,(car new)))
,setter))))
(defsetf frob (x) (value)
(setf (car ,x) ,value)) *!* FROB
;;; The following is an error; an error might be signaled at macro expansion time (flet ((frob (x) (cdr x))) ;Invalid
(xpop (frob z)))

See Also:

defsetf, define-setf-expander, setf

Notes:

Any compound form is a valid place, since any compound form whose operator f has no setf expander are expanded into a call to (setf f ).

Expanded Reference: get-setf-expansion

Examining the Five Values

get-setf-expansion returns five values that describe how to read and write a given place: temporary variables, value forms, store variables, the storing form, and the accessing form.

;; Expand a simple variable place
(get-setf-expansion 'x)
;; => NIL
;; => NIL
;; => (#:NEW-0)
;; => (SETQ X #:NEW-0)
;; => X
;; (Five values: no temps needed, one store var, setq to store, x to access)

Expansion for an Accessor

For accessor forms like (car lst), the expansion includes temporary bindings to avoid multiple evaluation.

(multiple-value-bind (temps vals stores store-form access-form)
(get-setf-expansion '(car my-list))
(list :temps temps
:vals vals
:stores stores
:store-form store-form
:access-form access-form))
;; => (:TEMPS (#:G0) :VALS (MY-LIST) :STORES (#:G1)
;; :STORE-FORM (SB-KERNEL:%RPLACA #:G0 #:G1) :ACCESS-FORM (CAR #:G0))
;; (implementation-dependent details)

Using the Expansion in a Macro

The five values from get-setf-expansion are used when writing macros that need to properly read-modify-write a place.

;; A macro that increments a place and returns the old value (like post-increment)
(defmacro post-incf (place &environment env)
(multiple-value-bind (temps vals stores store-form access-form)
(get-setf-expansion place env)
(let ((old (gensym "OLD")))
`(let* (,@(mapcar #'list temps vals)
(,old ,access-form)
(,(car stores) (1+ ,old)))
,store-form
,old))))

(let ((x 5))
(values (post-incf x) x))
=> 5
=> 6

Expansion for Nested Places

get-setf-expansion handles arbitrarily nested setf-able places.

(multiple-value-bind (temps vals stores store-form access-form)
(get-setf-expansion '(aref vec (+ i 1)))
(declare (ignore stores store-form access-form))
(list :num-temps (length temps)
:num-vals (length vals)))
=> (:NUM-TEMPS 2 :NUM-VALS 2)