User Tools


This shows you the differences between two versions of the page.

Link to this comparison view

cl:macros:defsetf [2019/07/14 17:00]
cl:macros:defsetf [2019/07/18 14:00] (current)
Line 1: Line 1:
 +====== Macro DEFSETF ======
 +The "short form":
 +  * **defsetf** //access-fn update-fn [documentation]//​ → //​access-fn//​
 +The "long form":
 +  * **defsetf** //access-fn lambda-list (store-variable''​*''​) <​nowiki>​[[</​nowiki>​declaration''​*''​ | documentation<​nowiki>​]]</​nowiki>​ form''​*''//​ → //​access-fn//​
 +====Arguments and Values====
 +  * //​access-fn//​ - a //​[[CL:​Glossary:​symbol]]//​ which names a //​[[CL:​Glossary:​function]]//​ or a //​[[CL:​Glossary:​macro]]//​.
 +  * //​update-fn//​ - a //​[[CL:​Glossary:​symbol]]//​ naming a //​[[CL:​Glossary:​function]]//​ or //​[[CL:​Glossary:​macro]]//​.
 +  * //​lambda-list//​ - a //​[[CL:​Glossary:​defsetf lambda list]]//.
 +  * //​store-variable//​ - a //​[[CL:​Glossary:​symbol]]//​ (a //​[[CL:​Glossary:​variable]]//​ //​[[CL:​Glossary:​name]]//​).
 +  * //​declaration//​ - a **[[CL:​Special Operators:​declare]]** //​[[CL:​Glossary:​expression]]//;​ not evaluated.
 +  * //​documentation//​ - a //​[[CL:​Glossary:​string]]//;​ not evaluated.
 +  * //form// - a //​[[CL:​Glossary:​form]]//​.
 +**defsetf** defines how to **[[CL:​Macros:​setf]]** a //​[[CL:​Glossary:​place]]//​ of the form ''​(access-fn ...)''​ for relatively simple cases. (See **[[CL:​Macros:​define-setf-expander]]** for more general access to this facility.)
 +It must be the case that the //​[[CL:​Glossary:​function]]//​ or //​[[CL:​Glossary:​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 //​[[CL:​Glossary:​type]]//​ of the second //​[[CL:​Glossary:​argument]]//​.
 +When the short form is used, //​update-fn//​ must name a //​[[CL:​Glossary:​function]]//​ (or //​[[CL:​Glossary:​macro]]//​) that takes one more argument than //​access-fn//​ takes. When **[[CL:​Macros:​setf]]** is given a //​[[CL:​Glossary:​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 **[[CL:​Macros:​defmacro]]**. The //​lambda-list//​ describes the arguments of //​access-fn//​. The //​store-variables//​ describe the value or values to be stored into the //​[[CL:​Glossary:​place]]//​. The //body// must compute the expansion of a **[[CL:​Macros:​setf]]** of a call on //​access-fn//​.
 +The expansion function is defined in the same //​[[CL:​Glossary:​lexical environment]]//​ in which the **defsetf** //​[[CL:​Glossary:​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 **[[CL:​Functions:​gensym]]** or **[[CL:​Functions:​gentemp]]**,​ that will be bound by the expansion of **[[CL:​Macros:​setf]]** to the values of those //​[[CL:​Glossary:​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 //​[[CL:​Glossary:​block]]//​ whose name is //​access-fn//​.
 +**defsetf** ensures that //​[[CL:​Glossary:​subforms]]//​ of the //​[[CL:​Glossary:​place]]//​ are evaluated exactly once.
 +//​documentation//​ is attached to //​access-fn//​ as a //​[[CL:​Glossary:​documentation string]]// of kind **[[CL:​Types:​setf]]**.
 +If a **defsetf** //​[[CL:​Glossary:​form]]//​ appears as a //​[[CL:​Glossary:​top level form]]//, the //​[[CL:​Glossary:​compiler]]//​ must make the //​[[CL:​Glossary:​setf expander]]//​ available so that it may be used to expand calls to **[[CL:​Macros:​setf]]** later on in the //​[[CL:​Glossary:​file]]//​. Users must ensure that the //forms//, if any, can be evaluated at compile time if the //​access-fn//​ is used in a //​[[CL:​Glossary:​place]]//​ later in the same //​[[CL:​Glossary:​file]]//​. The //​[[CL:​Glossary:​compiler]]//​ must make these //​[[CL:​Glossary:​setf expander|setf expanders]]//​ available to compile-time calls to **[[CL:​Functions:​get-setf-expansion]]** when its //​environment//​ argument is a value received as the //​[[CL:​Glossary:​environment parameter]]//​ of a //​[[CL:​Glossary:​macro]]//​.
 +The effect of ''​(defsetf [[CL:​Functions:​symbol-value]] [[CL:​Functions:​set]])''​ is built into the Common Lisp system. This causes the form ''​([[CL:​Macros:​setf]] ([[CL:​Functions:​symbol-value]] foo) fu)''​ to expand into ''​([[CL:​Functions:​set]] foo fu)''​.
 +Note that ''​(defsetf car rplaca)''​ would be incorrect because **[[CL:​Functions:​rplaca]]** does not return its last argument.
 +([[CL:​Macros:​defun]] middleguy (x) 
 +  ([[CL:​Functions:​nth]] ([[CL:​Functions:​truncate]] ([[CL:​Functions:​math-one-minus|1-]] ([[CL:​Functions:​list-length]] x)) 2) x)) <​r>​MIDDLEGUY</​r>​
 +([[CL:​Macros:​defun]] set-middleguy (x v) 
 +  ([[CL:​Macros:​unless]] ([[CL:​Functions:​null]] x) 
 +    ([[CL:​Functions:​rplaca]] ([[CL:​Functions:​nthcdr]] ([[CL:​Functions:​truncate]] ([[CL:​Functions:​math-one-minus|1-]] ([[CL:​Functions:​list-length]] x)) 2) x) v))
 +  v) <​r>​SET-MIDDLEGUY</​r>​
 +(defsetf middleguy set-middleguy) <​r>​MIDDLEGUY </r>
 +([[CL:​Macros:​defparameter]] *a* ([[CL:​Functions:​list]] 'a 'b 'c 'd)) <​r>​*A*</​r>​
 +([[CL:​Macros:​defparameter]] *b* ([[CL:​Functions:​list]] 'x)) <​r>​*B*</​r>​
 +([[CL:​Macros:​defparameter]] *c* ([[CL:​Functions:​list]] 1 2 3 ([[CL:​Functions:​list]] 4 5 6) 7 8 9)) <​r>​*C*</​r>​
 +([[CL:​Macros:​setf]] (middleguy *a*) 3) <r>3 </r>
 +([[CL:​Macros:​setf]] (middleguy *b*) 7) <r>7 </r>
 +([[CL:​Macros:​setf]] (middleguy (middleguy *c*)) '​middleguy-symbol) <​r>​MIDDLEGUY-SYMBOL </r>
 +*a* <r>(A 3 C D) </r>
 +*b* <​r>​(7) </r>
 +*c* <r>(1 2 3 (4 MIDDLEGUY-SYMBOL 6) 7 8 9) </r>
 +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)) <​r>​SUBSEQ</​r>​
 +;;; TODO this example is terrible, replace it
 +([[CL:​Macros:​defvar]] *xy* ([[CL:​Functions:​make-array]] '(10 10))) <​r>​*XY*</​r>​
 +([[CL:​Macros:​defun]] xy (&key ((x x) 0) ((y y) 0)) 
 +  ([[CL:​Functions:​aref]] *xy* x y)) <​r>​XY</​r>​
 +([[CL:​Macros:​defun]] set-xy (new-value &key ((x x) 0) ((y y) 0))
 +  ([[CL:​Macros:​setf]] ([[CL:​Functions:​aref]] *xy* x y) new-value)) <​r>​SET-XY</​r>​
 +(defsetf xy (&key ((x x) 0) ((y y) 0)) (store) ​
 +  `(set-xy ,store 'x ,x 'y ,y)) <​r>​XY</​r>​
 +([[CL:​Functions:​get-setf-expansion]] '(xy a b))
 +<​r>​(#:​t0 #:t1)
 +(a b)
 +(([[CL:​Symbols:​lambda]] (&key ((x #:x)) ((y #:y))) (set-xy #:store 'x #:x 'y #:y)) #:t0 #:t1)
 +(xy #:t0 #:​t1)</​r>​
 +(xy 'x 1) <​r>​[[CL:​Constant Variables:​NIL]]</​r>​
 +([[CL:​Macros:​setf]] (xy 'x 1) 1) <r>1 </r>
 +(xy 'x 1) <r>1 </r>
 +([[CL:​Special Operators:​let]] ((a 'x) (b '​y)) ​
 +  ([[CL:​Macros:​setf]] (xy a 1 b 2) 3) 
 +  ([[CL:​Macros:​setf]] (xy b 5 a 9) 14)) <r>14 </r>
 +(xy 'y 0 'x 1) <r>1 </r>
 +(xy 'x 1 'y 2) <r>3 </r>
 +An example involving //​[[CL:​Glossary:​structure|structures]]//​ and //​[[CL:​Glossary:​multiple values]]// in **defsetf**:​
 +([[CL:​Macros:​defstruct]] point x y z) <​r>​POINT</​r>​
 +([[CL:​Macros:​defsetf]] foo (point &key kind) (x y z)
 +  `([[CL:​Macros:​ecase]] ,kind
 +     (:2d ([[CL:​Macros:​setf]] ([[CL:​Functions:​values]] (point-x ,point)
 +                        (point-y ,point))
 +                ([[CL:​Functions:​values]] ,x ,y)))
 +     (:3d ([[CL:​Macros:​setf]] ([[CL:​Functions:​values]] (point-x ,point)
 +                        (point-y ,point)
 +                        (point-z ,point))
 +                ([[CL:​Functions:​values]] ,x ,y ,z))))) <​r>​FOO</​r>​
 +([[CL:​Macros:​defvar]] *point-1* (make-point)) <​r>​*POINT-1*</​r>​
 +([[CL:​Macros:​defvar]] *point-2* (make-point)) <​r>​*POINT-2*</​r>​
 +([[CL:​Macros:​setf]] (foo *point-1* :kind :2d) ([[CL:​Functions:​values]] 1 2)) 
 +<​r>​1 ​
 +([[CL:​Macros:​setf]] (foo *point-2* :kind :3d) ([[CL:​Functions:​values]] 8 9 10))
 +<​r>​8 ​
 +*point-1* <​r>#​S(POINT :X 1 :Y 2 :Z NIL)</​r>​
 +*point-2* <​r>#​S(POINT :X 8 :Y 9 :Z 10)</​r>​
 +====Affected By====
 +====Exceptional Situations====
 +====See Also====
 +  * **[[CL:​Functions:​documentation|Generic Function DOCUMENTATION]]**
 +  * **[[CL:​Macros:​setf|Macro SETF]]**
 +  * **[[CL:​Macros:​define-setf-expander|Macro DEFINE-SETF-EXPANDER]]**
 +  * **[[CL:​Functions:​get-setf-expansion|Function GET-SETF-EXPANSION]]**
 +  * {\secref\GeneralizedReference}
 +  * {\secref\DocVsDecls}
 +//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 //​[[CL:​Glossary:​place]]//​ and returns the correct value.
 +A **[[CL:​Macros:​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 //​[[CL:​Glossary:​generalized reference]]//​ to a byte, such as ''​(ldb field reference)''​.
 +**[[CL:​Macros:​define-setf-expander]]** is used to handle situations that do not fit the restrictions imposed by **defsetf** and gives the user additional control.