8

I am trying to call a macro (defined somewhere else; I can't change it), but I want to evaluate some of the arguments before the macro is expanded. Is this possible?

For example:

(defmacro foo (&rest args)
  (mapcar (lambda (a) (message "arg: %s" a)) args) nil)

(foo b (+ 1 1) c)
;; Prints:
;;   arg: b
;;   arg: (+ 1 1)
;;   arg: c
;; But I want:
;;   arg: b
;;   arg: 2
;;   arg: c

The only way I've found so far is:

(eval `(foo b ,(+ 1 1) c))

Is there a better way?

3
  • 4
    1. There's no way to ask an existing macro to evaluate its arguments when you call it (not doing so is one of the key properties of a macro). 2. Your example macro expands to nil and would trigger those messages only at expansion-time, so... that's weird.
    – phils
    Commented May 15, 2019 at 21:41
  • I agree that it's a bit weird. For context, I'm messing around with Doom Emacs' doom! macro, which I assume was written as such to avoid quoting all of its arguments. I'm trying to make some of my included packages conditional based on whether I am configured for running at work, at home, etc., but doom! is interpreting my (when ...) forms as package declarations. The eval trick above seems to hack around that.
    – 0x5453
    Commented May 16, 2019 at 13:30
  • 4
    How about (let ((tmp (+ 1 1))) (foo b tmp c))?
    – kdb
    Commented May 16, 2019 at 22:24

1 Answer 1

2

This is completely doable. Since a macro operates on lists, your parameters are similarly going to be a list. You can check if any of the elements in the argument list are lists themselves, and evaluate them:

(defmacro foo (&rest args)
  (mapcar
   #'(lambda (a)
       (message "arg: %s" (if (listp a) (eval a) a)))  ; Changed here...
   args)
  nil)

For clarity, I've added some spacing. Now, when I run this:

(foo b (+ 1 1) c)

... it shows:

arg: b
arg: 2
arg: c

Your addition example is trivial, and your real code is probably more complicated, so beware. This makes the potential for some instability if you want to pass in a list for some parameter. I suppose you could get even fancier, and also check if the car of the list is a function, and so on as needed.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.