Define-modify-macro with statement argument
In Section 12.4 incl. Lisp , Paul Graham writes: "Unfortunately, we cannot determine the correct _f
c define-modify-macro
, because the operator applied to the generic variable is given as an argument."
But what's wrong with that?
(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)
Perhaps there were changes made to the define-modify-macro in ANSI Common Lisp, which was not valid at the time Lisp was written? Or are there reasons other than those listed for not being used define-modify-macro
here?
It looks like Graham wants to make a call like
(_f * (second lst) 6)
but not
(_f #'* (second lst) 6)
But surely this is not in line with Lisp2 like Common Lisp?
source to share
According to Lispworks Hyperspec and CLtL2 (look for define-modify-macro
), a function is assumed to be a symbol (for a function or a macro). As far as I know, the following definition may be out of specification:
(define-modify-macro _f (op operand) (lambda (x op operand) (funcall op x operand)))
But it is of course possible that the implementation allows this. To make sure you follow the standard, you can define your own function, or even a macro:
(defmacro funcall-1 (val fun &rest args) `(funcall ,fun ,val ,@args)) (define-modify-macro _ff (&rest args) funcall-1) (let ((x (list 1 2 3 4))) (_ff (third x) #'+ 10) x)
If you want to have a function as the second argument, you can define another macro:
(defmacro ff (fun-form place &rest args)
`(_ff ,place ,fun-form ,@args))
Basically, your approach is to wrap funcall
in define-modify-macro
and give the desired function as an argument to that function. At first glance, this looks like a hack, but as we can see below, it gives the same macro-screen code as in On Lisp, assuming we modify it a little.
Macro Expression above:
(LET ((X (LIST 1 2 3 4))) (LET* ((#:G1164 X) (#:G1165 (FUNCALL #'+ (THIRD #:G1164) 10))) (SB-KERNEL:%RPLACA (CDDR #:G1164) #:G1165)) X)
The On Lisp version behaves like this:
(defmacro _f (op place &rest args) (multiple-value-bind (vars forms var set access) (get-setf-expansion place) `(let* (,@(mapcar #'list vars forms) (, (car var) (,op ,access ,@args))) ,set))) (let ((x (list 1 2 3 4))) (_f * (third x) 10) x)
Macroexpansion:
(LET ((X (LIST 1 2 3 4)))
(LET* ((#:G1174 X) (#:G1175 (* (THIRD #:G1174) 10)))
(SB-KERNEL:%RPLACA (CDDR #:G1174) #:G1175))
X)
This *
is injected directly by macros, which means the resulting code has no possible runtime overhead (although compilers will probably handle yours (funcall #'+ ...)
equally well). If you pass a macro #'+
, it will not be macro exposed. This is the main difference in your approach, but not a major limitation. For the On Lisp version to accept, #'*
or even (create-closure)
as an operator, it should be modified to read:
(defmacro _f (op place &rest args) (multiple-value-bind (vars forms var set access) (get-setf-expansion place) `(let* (,@(mapcar #'list vars forms) (, (car var) (funcall ,op ,access ,@args))) ,set)))
(see call funcall
)
The previous example is then extended as follows to #'*
:
(LET ((X (LIST 1 2 3 4))) (LET* ((#:G1180 X) (#:G1181 (FUNCALL #'* (THIRD #:G1180) 10))) (SB-KERNEL:%RPLACA (CDDR #:G1180) #:G1181)) X)
Now this is exactly the same as your version. Lisp is used _f
to demonstrate how to use get-setf-expansion
, and _f
is a good example for that. But on the other hand, your implementation seems to be equally good.
source to share
In the question of whether it is possible to transition *
or #'*
, we can also note that the version of the define-modify-macro
version _f
and @coredump adapted (with funcall
) take lambda forms in position op with or without #'
such as (lambda (x y) (* x y))
and #'(lambda (x y) (* x y))
, whereas the original version of Graham only accepts the former.
Interestingly, in his book Let over Lambda , Doug Hoyt draws attention to a comment by Graham in his ANSI Common Lisp that he can omit #'
before the lambda form provides "the best form of elegance at best" before opting to omit it.
I am by no means stopping by simply pointing out that given Graham's choice for _f
, the absence is #'
no longer indicative, but necessary.
source to share