Sometimes we need to modify a place but here is no built-in function that meets our needs.
For instance, here are incf
and decf
for addition and subtraction:
CL-USER> (defvar *x* 5)
*X*
CL-USER> (incf *x* 3)
8
CL-USER> *x*
8
CL-USER> (decf *x* 10)
-2
CL-USER> *x*
-2
But how about multiplication and division? What if we wish to modify a place with arbitrary function, like this:
(xf (lambda (x) ...) *x*)
xf
utility would be very useful, especially when we have to deal with deeply nested structures:
(my-accessor (aref (cdr *my-data*) n))
Using define-modify-macro (plus a bit)
Mark's answer provides a thorough way to do this from scratch, but this can actually be approximated with define-modify-macro, and done with define-modify-macro plus another macro:
To reverse the order, it's easy to define a macro xf that expands to an xxf call:
Making it better
The current version only accepts a single function as an argument. Many functions take additional arguments, though (e.g., additional required arguments, keyword arguments, and optional arguments). We can still handle those with define-modify-macro though:
Defining new macros with
define-modify-macro
One simple way to define new handy macros for our needs is
define-modify-macro
. This is a handy macro which can create other macros for us.We should supply name of new macro, list of parameters (not including place there) and symbol of function that will be used for processing.
Example of use:
However,
define-modify-macro
cannot be used for arbitrary processing. Here we have to take a look at other possibilities.Function
get-setf-expansion
Function
get-setf-expansion
does not create any macros, but provides information which we can use to write our own.As you can see, it returns a bunch of values, so it may be confusing at first sight. Let's try it on example:
Writing
xf
macroIt seems like now we've got all information to write our
xf
macro:Note, that
xf
macro takes evironment variable and pass it toget-setf-expansion
. This variable is needed to ensure that any lexical bindings or definitions established in the compilation environment are taken into account.Let's try it:
Expansion:
I hope this information is useful.
This answer is based on Paul Graham's On Lisp, section 12.4 More Complex Utilities.