That's actually some older code. get-setf-method
was actually replaced by get-setf-expansion
as described in Issue SETF-METHOD-VS-SETF-METHOD Writeup. So what you should be interested in these days is get-setf-expansion
. The values that it returns are the pieces of code that you need to safely store a value in a location. This is very important because it's very easy to write modyfing macros incorrectly.
As to why some of the values can be nil
, one of the examples in the documentation for get-setf-expansion
actually shows how some of the values can be nil
:
(get-setf-expansion 'x)
;=> NIL, NIL, (#:G0001), (SETQ X #:G0001), X
But what are those values? For that we need to look at the syntax of the documentation:
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.
Those five return values are described in 5.1.1.2 Setf Expansions:
List of temporary variables a list of symbols naming temporary variables to be bound sequentially, as if by let*, to values resulting
from value forms.
List of value forms a list of forms (typically, subforms of the place) which when evaluated yield the values to which the
corresponding temporary variables should be bound.
List of store variables a list of symbols naming temporary store variables which are to hold the new values that will be assigned to
the place.
Storing form a form which can reference both the temporary and the store variables, and which changes the value of the place and
guarantees to return as its values the values of the store variables,
which are the correct values for setf to return.
Accessing form a form which can reference the temporary variables, and which returns the value of the place.
So what do those values in the example mean?
(get-setf-expansion 'x)
;⇒ NIL, NIL, (#:G0001), (SETQ X #:G0001), X
To write to variable x
, we don't need any temporary storage, and since there are no temporary values, we don't need any forms to produce values for them. We can notice here that the first and second values are always lists, and they should always have the same length. The third value is a list of store variables. This is a list, because we can actually use setf
to modify multiple values, but in this case there's just one. The variables here are where the macro should actually store the new values for the place. Then, it's the writer-form (setq x #:g0001)
that will actually take care of putting the value in the place. x
, of course, is a simple way of reading the value.
As a more complex example, have a look at this transcript from SBCL:
CL-USER> (defstruct person
person-name)
;⇒ PERSON
CL-USER> (get-setf-expansion '(char (person-name (first (second list-of-list-of-persons))) 3))
; (#:TMP965)
; ((PERSON-NAME (FIRST (SECOND LIST-OF-LIST-OF-PERSONS))))
; (#:NEW964)
; (SB-KERNEL:%CHARSET #:TMP965 3 #:NEW964)
; (CHAR #:TMP965 3)
This means that if we wanted to change the fourth character of the name of the first person in the second list of persons in a list of lists of persons, we could do it with:
(let* ((temp965 (person-name (first (second list-of-list-of-persons))))
(old-char (char tmp965 3))) ; optional
(setq new964 <compute-new-value>)
(sb-kernel:%charset tmp965 3 new964))
We can compute the new value however we want (just fill in for <compute-new-value>
), and we can even reference the old value if we want to (by including the optional line). All we need to do is set new964
to the new value, and then execute the writer-form that was given to us.
There are more examples of get-setf-expansion
on Stack Overflow: