Avoiding redundant place calculations with setf
common-lisp
Solution
(setf (gethash key table)
(my-transformation (gethash key table)))
Let's say the transformation is `1+`. Then above is
(setf (gethash key table)
(1+ (gethash key table)))
Which in Common Lisp has a macro:
(incf (gethash key table))
Clozure CL expands this to:
(LET* ((#:G69020 KEY)
(#:G69021 TABLE)
(#:G69022 1)
(#:G69019 (+ (GETHASH #:G69020 #:G69021) #:G69022)))
(DECLARE (TYPE BIT #:G69022) (TYPE T #:G69019))
(CCL::PUTHASH #:G69020 #:G69021 #:G69019))
Now remember that the setter for a place is found at compile time and that the setter operation of a place is different from the getter. Not only does the setter something different by updating something, but it also may do arbitrary other things like setting up a transaction, acquiring a lock, ... Thus getting and setting are not the same/similar operation twice.
Common Lisp has no physical data structure which represents a place. A place is an abstract idea which combines a setter with a given getter. The setter can be arbitrary complex code.
So, in above code - which calculations are redundant?
Still you say what about optimizing access for the primitive case:
(incf (car (very-long-access-chain data)))
You would have to write by hand:
(let ((cons-cell (very-long-access-chain data)))
(incf (car cons-cell))
Which is basically what Lisp does anyway. See: CLHS 5.1.1.1 Evaluation of Subforms to Places
Problem
The Context I've found in my limited experience with Common Lisp, it's not uncommon to have some code like this ``` (setf (gethash key table) (my-transformation (gethash key table))) ``` where you read from a `setf`able place, perform some computation on the value stored there, then write to the same place. The thing I don't like about this is that the place will be computed twice, but the place will (hopefully!) be the same both times. If the place computation is expensive, we're doing as much as twice as much work as we need to. The Question Is it possible to eliminate the need for the double computation? That is, is it possible (desirable?) to write a macro `setf-inplace`: ``` (setf-inplace (gethash key table) #'my-transformation) ``` conceptually equivalent (ignoring for the moment `multiple-values` weirdness) to but potentially much faster than the original code, preferably without relying on implementation details? I know I may be putting the cart in front of the horse in this particular case, since SBCL caches the lookup for `gethash`, but it seems to me that other `setf`able places, like `(assoc key my-alist)`, may not be so easy to cache—except, of course, via some mechanism like the `setf-inplace` above.