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?

+3


source to share


2 answers


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.

+2


source


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.

+1


source







All Articles