2

Is there a way to expand a macro using a list of arguments? I tried using apply but then I get an error that the "function" my/x-becomes-nil is invalid.

(defmacro my/x-becomes-nil (variable x)
  `(if (eq ,variable ,x)
       (setq ,variable nil)))

(let ((q 2))
  (my/x-becomes-nil q 2)
  ;;(apply 'my/x-becomes-nil (list q 2)) ;; How to make this work?
  q)
Drew
  • 75,699
  • 9
  • 109
  • 225
Tohiko
  • 1,589
  • 1
  • 10
  • 22
  • 1
    Since macro arguments are not evaluated, "dynamic" macro calls in general can only happen from within other macro bodies, or through the use of [`eval`](https://www.gnu.org/software/emacs/manual/html_node/elisp/Eval.html). – Basil Feb 01 '21 at 15:45
  • 3
    Does this answer your question? [Inconsistent behavior when calling #'funcall with a macro](https://emacs.stackexchange.com/questions/59066/inconsistent-behavior-when-calling-funcall-with-a-macro) – NickD Feb 01 '21 at 18:59
  • 1
    Also similar to a recent question about special forms https://emacs.stackexchange.com/q/62727 – phils Feb 01 '21 at 19:30

2 Answers2

3

No. As (elisp) Calling Functions tells us, for funcall (and it lets us know that apply is the same):

Special forms and macros are not allowed, because they make sense only when given the unevaluated argument expressions. funcall cannot provide these because, as we saw above, it never knows them in the first place.

I suggest that you think about what you are really trying to do, i.e., the reason why you think you need/want to apply a macro to a list of args. There might be an X-Y problem here.

Drew
  • 75,699
  • 9
  • 109
  • 225
  • I see. I don't quite see why the macro call is not possible since the arguments are not evaluated (I have a list of arguments or forms) so substituting the list of arguments into the macro definition should be possible theoretically. – Tohiko Feb 01 '21 at 15:56
  • 3
    `funcall` and `apply` are themselves functions -- so by the time they call their function argument, the arguments to be passed to that function have likewise already been evaluated. I.e. if you call `(apply 'foo X Y Z)` then `'foo`, `X`, `Y`, and `Z` have all been evaluated before `apply` does anything at all. This is the "it never knows them in the first place" part of the quote -- the *un*-evaluated arguments are not seen by `apply` at all. – phils Feb 02 '21 at 06:09
0
(defmacro my/x-becomes-nil (&rest args)
  (let* ((two-args (= (length args) 2))
         (var (funcall (if two-args #'car #'caar) args))
         (val (funcall (if two-args #'cadr #'cadar) args)))
    `(when (eq ,var ,val)
       (setq ,var nil))))

Expands identically when called either as (my/x-becomes-nil q 2) or (my/x-becomes-nil (q 2)). Note that the list literal in the second case is not quoted.

Phil Hudson
  • 1,651
  • 10
  • 13