I'm trying to write a macro that expands to an unspecified number of function calls, but I also want to be able to specify exactly one argument to be passed to each function in the macro call. I would basically want its syntax to look like a let
call:
(let ((var1 1)
(var2 2)
...
(varn n))
body)
but passing arguments to functions instead of binding variables, so a call like this
(foo ((fn1 arg1)
(fn2 arg2)
...
(fnn argn))
body)
expands to this:
(progn
(funcall fn1 arg1)
(funcall fn2 arg2)
...
(funcall fnn argn)
body)
As far as I can tell, the list-destructuring behaviour of macro lambda lists allows me to pass an unspecified number of forms to the macro in a nested lambda list:
(defmacro foo ((&rest call-forms) &body body)
...)
OR define a rigid syntax for a form in a nested lambda list:
(defmacro foo ((single-fn single-arg) &body body)
...)
but NOT both:
(defmacro foo ((&rest (fn arg)) &body body) ; gibberish
...)
Is there a loophole or workaround I'm not seeing? I know it seems arbitrary, but the call syntax I specified above would be ideal for what I'm doing. I get that this might be out of the question, since let
is a special operator and its behaviour appears to be unique, but I'd love to be proven wrong.
Maybe you can completely drop lambda-list destructuring... I suggest this solution:
(defmacro funcall-macro (pairs &body body)
(handler-case `(progn ,@(mapcar (lambda (pair)
(destructuring-bind (fname arg) pair
(list 'funcall fname arg)))
pairs)
,@body)
(error (e) (error (format nil "~a~%" e)))))
With destructuring-bind
, I check that each pair has exactly two elements.
Test:
(macroexpand-1 '(funcall-macro ((f1 a1)
(f2 a2)
(fn an))
(b1)
(b2)
(bn)))
(PROGN (FUNCALL F1 A1) (FUNCALL F2 A2) (FUNCALL FN AN) (B1) (B2) (BN))
T
This macro also works for pairs
equal to ()
.