In Section 12.4 of On Lisp, Paul Graham writes, "Unfortunately, we can't define a correct _f
with define-modify-macro
, because the operator to be applied to the generalized variable is given as an argument."
But what's wrong with something like this?
(define-modify-macro _f (op operand)
(lambda (x op operand)
(funcall op x operand)))
(let ((lst '(1 2 3)))
(_f (second lst) #'* 6)
lst)
=> (1 12 3)
Has there perhaps been a change made to define-modify-macro in ANSI Common Lisp that wasn't valid at the time On Lisp was written? Or are there reasons other than the one stated for not using define-modify-macro
here?
It appears that Graham want's to be able to make a call such as
(_f * (second lst) 6)
rather than
(_f #'* (second lst) 6)
But surely that's not in keeping with a Lisp2 such as Common Lisp?
According to both Lispworks's Hyperspec and CLtL2 (look for
define-modify-macro
), the function is assumed to be a symbol (to a function or a macro). As far as I know, the following definition might not be conforming the specification:But of course, it is possible that an implementation allows it. To be sure you are conforming to the standard, you can define your own function, or even a macro:
If you wanted to have the function as a second argument, you could define another macro:
Basically, your approach consists in wrapping
funcall
indefine-modify-macro
, and give the desired function as an argument of that function. At first sight, it looks like a hack, but as we can see below, this gives the same macroexanded code as the one in On Lisp, assuming we modify the latter a little.The macroexpansion of the above is:
The version in On Lisp behaves as follows:
Macroexpansion:
Here, the
*
is injected directly by the macroexpansion, which means that the resulting code has no possible runtime overhead (though compilers would probably handle your(funcall #'+ ...)
equally well). If you pass#'+
to the macro, it fails to macroexpand. This is the major difference with your approach, but not a big limitation. In order to allow the On Lisp version to accept#'*
, or even(create-closure)
as an operator, it should be modified as follows:(see the call to
funcall
)The previous example is then expanded as follows, for
#'*
:Now, it is exactly as your version. On Lisp uses
_f
to demonstrate how to useget-setf-expansion
, and_f
is a good example for that. But on the other hand, your implementation seems equally good.