7.2 Обобщённые переменные

В Lisp’е, переменная может запомнить одну часть данных, а точнее, один Lisp объект. Главные операции над переменной это получить её значение и задать ей другое значение. Их часто называют операциями доступа и изменения. Концепция переменных с именем в виде символа может быть обобщена до того, что любое место может сохранять в себе части данных вне зависимости от того, как данное место именуется. Примерами таких мест хранения являются car и cdr элементы cons-ячейки, элементы массива, и компоненты структуры.

Для каждого вида обобщённых переменных существуют две функции, которые реализуют операции доступа и изменения. Для переменных это имя переменной для доступа, а для изменения оператор setq. Функция car получает доступ к car элементу cons-ячейки, а функция rplaca изменяет этот элемент ячейки. Функция symbol-value получает динамическое значение переменной именованной некоторым символом, а функция set изменяет эту переменную.

Вместо того, чтобы думать о двух разных функциях, которые соответственно получают доступ и изменяют некоторое место хранения в зависимости от своих аргументов, мы может думать просто о вызове функции доступа с некоторыми аргументами, как о имени данного места хранения. Таким образом, просто x является именем места хранения (переменной), (car x) имя для car элементы для некоторой cons-ячейки (которая в свою очередь именуется символом x). Теперь вместо того, чтобы запоминать по две функции для каждого вида обобщённых переменных (например rplaca для car), мы адаптировали единый синтаксис для изменения некоторого места хранения с помощью макроса setf. Это аналогично способу, где мы используем специальную форму setq для преобразования имени переменной (которая является также формой для доступа к ней) в форму, которая изменяет переменную FIXME. Эта универсальной отображения в следующей таблице.

Функция доступа  Функция измененияИзменения с помощью setf
x  (setq x datum) (setf x datum)
(car x)  (rplaca x datum) (setf (car x) datum)
(symbol-value x) (set x datum) (setf (symbol-value x) datum)



 

setf это макрос, который анализирует форму доступа, и производит вызов соответствующей функции изменения.

С появление в Common Lisp’е setf, необходимость в setq, rplaca и set отпала. Они оставлены в Common Lisp из-за их исторической важности в Lisp’е. Однако, большинство других функций изменения (например putprop, функция изменения для get) были устранены из Common Lisp’а в расчёте на то, что везде на их месте будет использоваться setf.

[Макрос] setf {place newvalue}*

(setf place newvalue) принимает форму place, которая при своём вычислении получает доступ к объекту в некотором месте хранения и «инвертирует» эту форму в соответствующую форму изменения. Таким образом вызов макроса setf разворачивается в форму изменения, которая сохраняет результат вычисления формы newvalue в место хранения, на которое ссылалась форма доступа.

Если пар place-newvalue указано более одной, эти пары обрабатываются последовательно. Таким образом:

(setf place1 newvalue1
      place2 newvalue2)
      ...
      placen newvaluen)

эквивалентно

(setf place1 newvalue1
      place2 newvalue2)
      ...
      placen newvaluen)

Следует отметить, что запись (setf) является корректной и возвращает nil.

Форма place может быть одной из следующих:

setf тщательно сохраняет обычный порядок выполнения подформ слева направо. С другой стороны, точное раскрытие для какой-нибудь частной формы не гарантируется и может зависеть от реализации. Все, что гарантируется, это раскрытие setf формы в некоторую функцию изменения, используемую данной реализацией, и выполнение подформ слева направо.

Конечным результатом вычисления формы setf является значение newvalue. Таким образом (setf (car x) y) раскрывается не прямо в (rplaca x y), а в что-то вроде

(let ((G1 x) (G2 y)) (rplaca G1 G2) G2)

точное раскрытие зависит от реализации.

Пользователь может определить новое раскрытие для setf используя defsetf.

X3J13 voted in June 1989 to extend the specification of setf to allow a place whose setf method has more than one store variable (see define-setf-method). In such a case as many values are accepted from the newvalue form as there are store variables; extra values are ignored and missing values default to nil, as is usual in situations involving multiple values.

A proposal was submitted to X3J13 in September 1989 to add a setf method for values so that one could in fact write, for example,

(setf (values quotient remainder)
      (truncate linewidth tabstop))

but unless this proposal is accepted users will have to define a setf method for values themselves (not a difficult task).


[Макрос] psetf {place newvalue}*

psetf похожа на setf за исключением того, что если указано более одной пары place-newvalue , то присваивание местам новых значений происходит параллельно. Если говорить точнее, то все подформы, которые должны быть вычислены, вычисляются слева направо. После выполнения всех вычислений, выполняются все присваивания в неопределённом порядке. (Неопределённый порядок влияет на поведение в случае, если более одной формы place ссылаются на одно и то же место.) psetf всегда возвращает nil.

X3J13 voted in June 1989 to extend the specification of psetf to allow a place whose setf method has more than one store variable (see define-setf-method). In such a case as many values are accepted from the newvalue form as there are store variables; extra values are ignored and missing values default to nil, as is usual in situations involving multiple values.


[Макрос] shiftf {place}+ newvalue

Каждая форма place может быть любой обобщённой переменной, как для setf. В форме (shiftf place1 place2 ... placen newvalue), вычисляются и сохраняются значения с place1 по placen и вычисляется newvalue, как значение с номером n + 1. Значения с 2-го по n + 1 сохраняются в интервале с place1 по placen и возвращается 1-ое значение (оригинальное значение place1). Механизм работает как сдвиг регистров. newvalue сдвигается с правой стороны, все значения сдвигаются влево на одну позицию, и возвращается сдвигаемое самое левое значение place1. Например:

(setq x (list ’a ’b ’c))  (a b c)

(shiftf (cadr x) ’z)  b
   and now x  (a z c)

(shiftf (cadr x) (cddr x) ’q)  z
   and now x  (a (c) . q)

Эффект от (shiftf place1 place2 ... placen newvalue) эквивалентен

(let ((var1 place1)
      (var2 place2)
      ...
      (varn placen))
  (setf place1 var2)
  (setf place2 var3)
  ...
  (setf placen newvalue)
  var1)

за исключением того, что последний вариант выполняет все подформы для каждого place дважды, тогда как shiftf выполняет только единожды. Например:

(setq n 0)
(setq x ’(a b c d))
(shiftf (nth (setq n (+ n 1)) x) ’z)  b
   теперь x  (a z c d)
but
(setq n 0)
(setq x ’(a b c d))
(prog1 (nth (setq n (+ n 1)) x)
       (setf (nth (setq n (+ n 1)) x) ’z))  b
   и теперь x  (a b z d)

Более того, для заданных форм place shiftf может быть более производительной, чем версия с prog1.

X3J13 voted in June 1989 to extend the specification of shiftf to allow a place whose setf method has more than one store variable (see define-setf-method). In such a case as many values are accepted from the newvalue form as there are store variables; extra values are ignored and missing values default to nil, as is usual in situations involving multiple values.

__________________________________________________________________________

Обоснование: shiftf and rotatef have been included in Common Lisp as generalizations of two-argument versions formerly called swapf and exchf. The two-argument versions have been found to be very useful, but the names were easily confused. The generalization to many argument forms and the change of names were both inspired by the work of Suzuki [47], which indicates that use of these primitives can make certain complex pointer-manipulation programs clearer and easier to prove correct.

___________________________________________________________________________________________________________

[Макрос] rotatef {place}*

Каждая place может быть обобщённой переменной, как для setf. В форме (rotatef place1 place2 ... placen), вычисляются и сохраняются значения c place1 по placen. Механизм действует как круговой сдвиг регистров влево, и значение place1 сдвигается в конец на placen. Следует отметить, что (rotatef place1 place2) меняет значения между place1 и place2.

Эффект от использования (rotatef place1 place2 ... placen) эквивалентен

(psetf place1 place2
       place2 place3
       ...
       placen place1)

за исключением того, что в последнем вычисление форм происходит дважды, тогда как rotatef выполняет только единожды. Более того, для заданных форм place rotatef может быть более производительной, чем версия с prog1.

rotatef всегда возвращает nil.

X3J13 voted in June 1989 to extend the specification of rotatef to allow a place whose setf method has more than one store variable (see define-setf-method). In such a case as many values are accepted from the newvalue form as there are store variables; extra values are ignored and missing values default to nil, as is usual in situations involving multiple values.


Другие макросы, которые управляют обобщёнными переменными, включают getf, remf, incf, decf, push, pop, assert, ctypecase и ccase.

Макросы, которые управляют обобщёнными переменными, должны гарантировать «явную» семантику: подформы обобщённых переменных вычисляются точно столько раз, сколько они встречаются в выражении, и в том же порядке, в котором встречаются.

В ссылках на обобщённые переменные, как в shiftf, incf, push и setf или ldb, обобщённые переменные считываются и записываются в одну и ту же ссылку. Сохранение порядка выполнения исходной программы и количества выполнений чрезвычайно важно.

В качестве примера этих семантических правил, в ссылке на обобщённую переменную (setf reference value) форма value должны быть вычислена после всех подформ данной ссылки, потому что форма value стоит правее всех.

The expansion of these macros must consist of code that follows these rules or has the same effect as such code. This is accomplished by introducing temporary variables bound to the subforms of the reference. As an optimization in the implementation, temporary variables may be eliminated whenever it can be proved that removing them has no effect on the semantics of the program. For example, a constant need never be saved in a temporary variable. A variable, or for that matter any form that does not have side effects, need not be saved in a temporary variable if it can be proved that its value will not change within the scope of the generalized-variable reference.

Common Lisp provides built-in facilities to take care of these semantic complications and optimizations. Since the required semantics can be guaranteed by these facilities, the user does not have to worry about writing correct code for them, especially in complex cases. Even experts can become confused and make mistakes while writing this sort of code.

X3J13 voted in March 1988 to clarify the preceding discussion about the order of evaluation of subforms in calls to setf and related macros. The general intent is clear: evaluation proceeds from left to right whenever possible. However, the left-to-right rule does not remove the obligation on writers of macros and define-setf-method to work to ensure left-to-right order of evaluation.

Let it be emphasized that, in the following discussion, a form is something whose syntactic use is such that it will be evaluated. A subform means a form that is nested inside another form, not merely any Lisp object nested inside a form regardless of syntactic context.

The evaluation ordering of subforms within a generalized variable reference is determined by the order specified by the second value returned by get-setf-method. For all predefined generalized variable references (getf, ldb), this order of evaluation is exactly left-to-right. When a generalized variable reference is derived from a macro expansion, this rule is applied after the macro is expanded to find the appropriate generalized variable reference.

This is intended to make it clear that if the user writes a defmacro or define-setf-method macro that doesn’t preserve left-to-right evaluation order, the order specified in the user’s code holds. For example, given

(defmacro wrong-order (x y) ‘(getf ,y ,x))

then

(push value (wrong-order place1 place2))

will evaluate place2 first and then place1 because that is the order they are evaluated in the macro expansion.

For the macros that manipulate generalized variables (push, pushnew, getf, remf, incf, decf, shiftf, rotatef, psetf, setf, pop, and those defined with define-modify-macro) the subforms of the macro call are evaluated exactly once in left-to-right order, with the subforms of the generalized variable references evaluated in the order specified above.

Each of push, pushnew, getf, remf, incf, decf, shiftf, rotatef, psetf, and pop evaluates all subforms before modifying any of the generalized variable locations. Moreover, setf itself, in the case when a call on it has more than two arguments, performs its operation on each pair in sequence. That is, in

(setf place1 value1 place2 value2 ...)

the subforms of place1 and value1 are evaluated, the location specified by place1 is modified to contain the value returned by value1, and then the rest of the setf form is processed in a like manner.

For the macros check-type, ctypecase, and ccase, subforms of the generalized variable reference are evaluated once per test of a generalized variable, but they may be evaluated again if the type check fails (in the case of check-type) or if none of the cases holds (in ctypecase or ccase).

For the macro assert, the order of evaluation of the generalized variable references is not specified.

Another reason for building in these functions is that the appropriate optimizations will differ from implementation to implementation. In some implementations most of the optimization is performed by the compiler, while in others a simpler compiler is used and most of the optimization is performed in the macros. The cost of binding a temporary variable relative to the cost of other Lisp operations may differ greatly between one implementation and another, and some implementations may find it best never to remove temporary variables except in the simplest cases.

A good example of the issues involved can be seen in the following generalized-variable reference:

(incf (ldb byte-field variable))

This ought to expand into something like

(setq variable
      (dpb (1+ (ldb byte-field variable))
           byte-field
           variable))

In this expansion example we have ignored the further complexity of returning the correct value, which is the incremented byte, not the new value of variable. Note that the variable byte-field is evaluated twice, and the variable variable is referred to three times: once as the location in which to store a value, and twice during the computation of that value.

Now consider this expression:

(incf (ldb (aref byte-fields (incf i))
           (aref (determine-words-array) i)))

It ought to expand into something like this:

(let ((temp1 (aref byte-fields (incf i)))
      (temp2 (determine-words-array)))
  (setf (aref temp2 i)
        (dpb (1+ (ldb temp1 (aref temp2 i)))
             temp1
             (aref temp2 i))))

Again we have ignored the complexity of returning the correct value. What is important here is that the expressions (incf i) and (determine-words-array) must not be duplicated because each may have a side effect or be affected by side effects.

X3J13 voted in January 1989 to specify more precisely the order of evaluation of subforms when setf is used with an access function that itself takes a place as an argument, for example, ldb, mask-field, and getf. (The vote also discussed the function char-bit, but another vote removed that function from the language.) The setf methods for such accessors produce expansions that effectively require explicit calls to get-setf-method.

The code produced as the macro expansion of a setf form that itself admits a generalized variable as an argument must essentially do the following major steps:

Doing the access for a generalized variable reference is not part of the series of evaluations that must be done in left-to-right order.

The place-specifier forms ldb, mask-field, and getf admit (other) place specifiers as arguments. During the setf expansion of these forms, it is necessary to call get-setf-method to determine how the inner, nested generalized variable must be treated.

In a form such as

(setf (ldb byte-spec place-form) newvalue-form)

the place referred to by the place-form must always be both accessed and updated; note that the update is to the generalized variable specified by place-form, not to any object of type integer.

Thus this call to setf should generate code to do the following:

If the evaluation of newvalue-form alters what is found in the given place—such as setting a different bit-field of the integer—then the change of the bit-field denoted by byte-spec will be to that altered integer, because the access step must be done after the newvalue-form evaluation. Nevertheless, the evaluations required for binding the temporaries are done before the evaluation of the newvalue-form, thereby preserving the required left-to-right evaluation order.

The treatment of mask-field is similar to that of ldb.

In a form such as:

(setf (getf place-form ind-form) newvalue-form)

the place referred to by the place-form must always be both accessed and updated; note that the update is to the generalized variable specified by place-form, not necessarily to the particular list which is the property list in question.

Thus this call to setf should generate code to do the following:

If the evaluation of newvalue-form alters what is found in the given place—such as setting a different named property in the list—then the change of the property denoted by ind-form will be to that altered list, because the access step is done after the newvalue-form evaluation. Nevertheless, the evaluations required for binding the temporaries are done before the evaluation of the newvalue-form, thereby preserving the required left-to-right evaluation order.

Note that the phrase “possibly new property list” treats the implementation of property lists as a “black box”; it can mean that the former property list is somehow destructively re-used, or it can mean partial or full copying of it. A side effect may or may not occur; therefore setf must proceed as if the resultant property list were a different copy needing to be stored back into the generalized variable.

The Common Lisp facilities provided to deal with these semantic issues include:

Also important are the changes that allow lexical environments to be used in appropriate ways in setf methods.

[Макрос] define-modify-macro name lambda-list function [doc-string]

This macro defines a read-modify-write macro named name. An example of such a macro is incf. The first subform of the macro will be a generalized-variable reference. The function is literally the function to apply to the old contents of the generalized-variable to get the new contents; it is not evaluated. lambda-list describes the remaining arguments for the function; these arguments come from the remaining subforms of the macro after the generalized-variable reference. lambda-list may contain &optional and &rest markers. (The &key marker is not permitted here; &rest suffices for the purposes of define-modify-macro.) doc-string is documentation for the macro name being defined.

The expansion of a define-modify-macro is equivalent to the following, except that it generates code that follows the semantic rules outlined above.

(defmacro name (reference . lambda-list)
  doc-string
  ‘(setf ,reference
         (function ,reference ,arg1 ,arg2 ...)))

where arg1, arg2, ..., are the parameters appearing in lambda-list; appropriate provision is made for a &rest parameter.

As an example, incf could have been defined by:

(define-modify-macro incf (&optional (delta 1)) +)

An example of a possibly useful macro not predefined in Common Lisp is

(define-modify-macro unionf (other-set &rest keywords) union)

X3J13 voted in March 1988 to specify that define-modify-macro creates macros that take &environment arguments and perform the equivalent of correctly passing such lexical environments to get-setf-method in order to correctly maintain lexical references.


[Макрос] defsetf access-fn {update-fn [doc-string] | lambda-list (store-variable) [[{declaration}* | doc-string]] {form}*}

This defines how to setf a generalized-variable reference of the form (access-fn ...). The value of a generalized-variable reference can always be obtained simply by evaluating it, so access-fn should be the name of a function or a macro.

The user of defsetf provides a description of how to store into the generalized-variable reference and return the value that was stored (because setf is defined to return this value). The implementation of defsetf takes care of ensuring that subforms of the reference are evaluated exactly once and in the proper left-to-right order. In order to do this, defsetf requires that access-fn be a function or a macro that evaluates its arguments, behaving like a function. Furthermore, a setf of a call on access-fn will also evaluate 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 variable that is a byte, such as (ldb field reference). To handle situations that do not fit the restrictions imposed by defsetf, use define-setf-method, which gives the user additional control at the cost of increased complexity.

A defsetf declaration may take one of two forms. The simple form is

(defsetf access-fn update-fn [doc-string] )

The 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). For example, the effect of

(defsetf symbol-value set)

is built into the Common Lisp system. This causes the expansion

(setf (symbol-value foo) fu)  (set foo fu)

for example. Note that

(defsetf car rplaca)

would be incorrect because rplaca does not return its last argument.

The complex form of defsetf looks like

(defsetf access-fn lambda-list (store-variable) . body)

and resembles defmacro. The body must compute the expansion of a setf of a call on access-fn.

The lambda-list describes the arguments of access-fn. &optional, &rest, and &key markers are permitted in lambda-list. Optional arguments may have defaults and “supplied-p” flags. The store-variable describes the value to be stored into the generalized-variable reference. _____________________________________

Обоснование: The store-variable is enclosed in parentheses to provide for an extension to multiple store variables that would receive multiple values from the second subform of setf. The rules given below for coding setf methods discuss the proper handling of multiple store variables to allow for the possibility that this extension may be incorporated into Common Lisp in the future.

___________________________________________________________________________________________________________

The body forms can be written as if the variables in the lambda-list were bound to subforms of the call on access-fn and the store-variable were bound to the second subform of setf. However, this is not actually the case. During the evaluation of the body forms, these 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 body 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. In other words, an attempt is made by defsetf to generate the best code possible in a particular implementation.

Note that the code generated by the body forms must include provision for returning the correct value (the value of store-variable). This is handled by the body 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 generalized variable and returns the correct value.

An example of the use of the complex form of defsetf:

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

X3J13 voted in March 1988 to specify that the body of the expander function defined by the complex form of defsetf is implicitly enclosed in a block construct whose name is the same as the name of the access-fn. Therefore return-from may be used to exit from the function.

X3J13 voted in March 1989 to clarify that, while defining forms normally appear at top level, it is meaningful to place them in non-top-level contexts; the complex form of defsetf must define the expander function within the enclosing lexical environment, not within the global environment.


The underlying theory by which setf and related macros arrange to conform to the semantic rules given above is that from any generalized-variable reference one may derive its “setf method,” which describes how to store into that reference and which subforms of it are evaluated.

Given knowledge of the subforms of the reference, it is possible to avoid evaluating them multiple times or in the wrong order. A setf method for a given access form can be expressed as five values:

The temporary variables will be bound to the values of the value forms as if by let*; that is, the value forms will be evaluated in the order given and may refer to the values of earlier value forms by using the corresponding variables.

The store variables are to be bound to the values of the newvalue form, that is, the values to be stored into the generalized variable. In almost all cases only a single value is to be stored, and there is only one store variable.

The storing form and the accessing form may contain references to the temporary variables (and also, in the case of the storing form, to the store variables). The accessing form returns the value of the generalized variable. The storing form modifies the value of the generalized variable and guarantees to return the values of the store variables as its values; these are the correct values for setf to return. (Again, in most cases there is a single store variable and thus a single value to be returned.) The value returned by the accessing form is, of course, affected by execution of the storing form, but either of these forms may be evaluated any number of times and therefore should be free of side effects (other than the storing action of the storing form).

The temporary variables and the store variables are generated names, as if by gensym or gentemp, so that there is never any problem of name clashes among them, or between them and other variables in the program. This is necessary to make the special operators that do more than one setf in parallel work properly; these are psetf, shiftf, and rotatef. Computation of the setf method must always create new variable names; it may not return the same ones every time.

Some examples of setf methods for particular forms:

[Макрос] define-setf-method access-fn lambda-list [[{declaration}* | doc-string]] {form}*

This defines how to setf a generalized-variable reference that is of the form (access-fn...). The value of a generalized-variable reference can always be obtained simply by evaluating it, so access-fn should be the name of a function or a macro.

The lambda-list describes the subforms of the generalized-variable reference, as with defmacro. The result of evaluating the forms in the body must be five values representing the setf method, as described above. Note that define-setf-method differs from the complex form of defsetf in that while the body is being executed the variables in lambda-list are bound to parts of the generalized-variable reference, not to temporary variables that will be bound to the values of such parts. In addition, define-setf-method does not have defsetf’s restriction that access-fn must be a function or a function-like macro; an arbitrary defmacro destructuring pattern is permitted in lambda-list.

By definition there are no good small examples of define-setf-method because the easy cases can all be handled by defsetf. A typical use is to define the setf method for ldb:

;;; SETF method for the form (LDB bytespec int).
;;; Recall that the int form must itself be suitable for SETF.
(define-setf-method ldb (bytespec int)
  (multiple-value-bind (temps vals stores
                        store-form access-form)
      (get-setf-method int)         ;Get SETF method for int
    (let ((btemp (gensym))          ;Temp var for byte specifier
          (store (gensym))          ;Temp var for byte to store
          (stemp (first stores)))   ;Temp var for int to store
      ;; Return the SETF method for LDB as five values.
      (values (cons btemp temps)    ;Temporary variables
              (cons bytespec vals)  ;Value forms
              (list store)          ;Store variables
              ‘(let ((,stemp (dpb ,store ,btemp ,access-form)))
                 ,store-form
                 ,store)                     ;Storing form
              ‘(ldb ,btemp ,access-form)     ;Accessing form
              ))))

X3J13 voted in March 1988 to specify that the &environment lambda-list keyword may appear in the lambda-list in the same manner as for defmacro in order to obtain the lexical environment of the call to the setf macro. The preceding example should be modified to take advantage of this new feature. The setf method must accept an &environment parameter, which will receive the lexical environment of the call to setf; this environment must then be given to get-setf-method in order that it may correctly use any locally bound setf method that might be applicable to the place form that appears as the second argument to ldb in the call to setf.

;;; SETF method for the form (LDB bytespec int).
;;; Recall that the int form must itself be suitable for SETF.
;;; Note the use of an &environment parameter to receive the
;;; lexical environment of the call for use with GET-SETF-METHOD.
(define-setf-method ldb (bytespec int &environment env)
  (multiple-value-bind (temps vals stores
                        store-form access-form)
      (get-setf-method int env)     ;Get SETF method for int
    (let ((btemp (gensym))          ;Temp var for byte specifier
          (store (gensym))          ;Temp var for byte to store
          (stemp (first stores)))   ;Temp var for int to store
      ;; Return the SETF method for LDB as five values.
      (values (cons btemp temps)    ;Temporary variables
              (cons bytespec vals)  ;Value forms
              (list store)          ;Store variables
              ‘(let ((,stemp (dpb ,store ,btemp ,access-form)))
                 ,store-form
                 ,store)                     ;Storing form
              ‘(ldb ,btemp ,access-form)     ;Accessing form
              ))))

X3J13 voted in March 1988 to specify that the body of the expander function defined by define-setf-method is implicitly enclosed in a block construct whose name is the same as the name of the access-fn. Therefore return-from may be used to exit from the function.

X3J13 voted in March 1989 to clarify that, while defining forms normally appear at top level, it is meaningful to place them in non-top-level contexts; define-setf-method must define the expander function within the enclosing lexical environment, not within the global environment.


X3J13 voted in March 1988 to add an optional environment argument to get-setf-method. The revised definition and example are as follows.

[Function] get-setf-method form &optional env

get-setf-method returns five values constituting the setf method for form. The form must be a generalized-variable reference. The env must be an environment of the sort obtained through the &environment lambda-list keyword; if env is nil or omitted, the null lexical environment is assumed. get-setf-method takes care of error checking and macro expansion and guarantees to return exactly one store variable.

As an example, an extremely simplified version of setf, allowing no more and no fewer than two subforms, containing no optimization to remove unnecessary variables, and not allowing storing of multiple values, could be defined by:

(defmacro setf (reference value &environment env)
  (multiple-value-bind (vars vals stores store-form access-form)
      (get-setf-method reference env)     ;Note use of environment
    (declare (ignore access-form))
    ‘(let* ,(mapcar #’list
                    (append vars stores)
                    (append vals (list value)))
       ,store-form)))


X3J13 voted in March 1988 to add an optional environment argument to get-setf-method. The revised definition is as follows.

[Function] get-setf-method-multiple-value form &optional env

get-setf-method-multiple-value returns five values constituting the setf method for form. The form must be a generalized-variable reference. The env must be an environment of the sort obtained through the &environment lambda-list keyword; if env is nil or omitted, the null lexical environment is assumed.

This is the same as get-setf-method except that it does not check the number of store variables; use this in cases that allow storing multiple values into a generalized variable. There are no such cases in standard Common Lisp, but this function is provided to allow for possible extensions.


X3J13 voted in March 1988 to clarify that a setf method for a functional name is applicable only when the global binding of that name is lexically visible. If such a name has a local binding introduced by flet, labels, or macrolet, then global definitions of setf methods for that name do not apply and are not visible. All of the standard Common Lisp macros that modify a setf place (for example, incf, decf, pop, and rotatef) obey this convention.