When not to use SETF-functions #
hacking, August 8th 2007
SETF-functions are really nice, but there is one downside to them:
(defvar *foo* (make-array 32)) (defun (setf foo) (value index symbol) (setf (getf (svref *foo* index) symbol) value)) (macroexpand '(setf (foo (get-index) (get-symbol)) (get-value))) => (LET* ((#:G2017 (GET-INDEX)) (#:G2016 (GET-SYMBOL))) (MULTIPLE-VALUE-BIND (#:G2015) (GET-VALUE) (FUNCALL #'(SETF FOO) #:G2015 #:G2017 #:G2016)))
A macroexpansion of this sort is necessary to preserve the evaluation order and still pass the result of GET-VALUE as the first argument.
In most cases there is nothing at all wrong with this, but imagine a SETF-function that you want to write a compiler macro for. Compiler macros are great when you can optimize calls that have arguments of a certain form — but in this case the compiler macro doesn't have anything useful to work on, because by the time it sees the call it has only a bunch of gensyms to play with!
While the SETF expander could try to be clever and figure out when it would be safe to just reorder the arguments, I don't recommend relying on that — there are always going to be interesting calls to optimize where the reordering is not going to be safe. Instead, I suggest you do something like this:
(defun set-foo (index symbol value) (setf (getf (svref *foo* index) symbol) value)) (defsetf foo set-foo) (macroexpand '(setf (foo (get-index) (get-symbol)) (get-value))) => (SET-FOO (GET-INDEX) (GET-SYMBOL) (GET-VALUE))
Now you can write a compiler-macro for SET-FOO, and it will always see the source forms it needs to see.