My goal is to have a macro that builds functions automatically with an argument list generated elsewhere. I want the macro to return a list composed of the function and the argument list it used (list of symbols). I'm using SBCL.
Generating the argument list
Let's say the argument list is generated by :
(defun input-syms ()
(list 'in1 'in2 'in3))
;;=> (IN1 IN2 IN3)
Building a function with this argument list
Following the useful answer in Nested `defun` produces a repeated warning in Allegro Common Lisp, I used labels
like so (adding the elements of a list, just for the sake of example) :
(defmacro create-funtest ()
(let ((input-list (input-syms)))
`(labels ((fun-created ,input-list
(reduce #'+ (list ,@input-list))))
#'fun-created)))
(funcall (create-funtest) 2 2 3) ;=> 7
This seems to work, although I think there could be a simpler way of doing this. (list ,@input-list)
seems unnecessary , but replacing it with just ,input-list
does not work.
Returning the argument list along with the function
This is where I am at a loss and it seems related to the issue of what exactly ,input-list
means. I think this has something to do with the fact that we're manipulating symbols so I tried throwing symbol-value
in there but to no avail.
What I would like to obtain, expressed in not-working code would be :
(defmacro create-funtest2 ()
(let ((input-list (input-syms)))
`(labels ((fun-created ,input-list
(reduce #'+ (list ,@input-list))))
(list #'fun-created ,input-list))))
Which must return : (#<FUNCTION ...> (IN1 IN2 IN3))
.
But calling create-funtest2
gives a compilation error The variable IN2 is unbound.
. I think it's trying to evaluate the symbol instead of giving me the symbol as is.
I need to be able to get the symbols that were used to build the function, I use them when calling the function afterwards to know which input is what. This list of symbols can also be modified by the create-funtest
macro, so I really need to get it from inside the macro.
Edit 1
Thanks Rainer Joswig for the answer. The thing that is bothering me is actually returning the symbol list as symbols. I guess the expanded code for create-funtest2
(from the expansion you've given) should look like :
(LABELS ((FUN-CREATED (IN1 IN2 IN3)
(REDUCE #'+ (LIST IN1 IN2 IN3))))
(LIST #'FUN-CREATED (LIST 'IN1 'IN2 'IN3)))
So that the output of the macro is (#<FUNCTION ...> (IN1 IN2 IN3))
.
The issue is that I want to evaluate input-list
, but keep its elements as symbols (not sure I'm wording that right sorry).
Edit 2
Thank you coredump. The working version of create-funtest2
is :
(defmacro create-funtest2 ()
(let ((input-list (test-input-syms)))
`(labels ((fun-created ,input-list
(reduce #'+ (list ,@input-list))))
(list #'fun-created (quote ,input-list)))))
Which gives the expansion (thank you Rainer for the code snippet):
(let ((*print-circle* t)
(*PRINT-RIGHT-MARGIN* 50))
(pprint (copy-tree (macroexpand-1 '(create-funtest2)))))
=>
(LABELS ((FUN-CREATED (IN1 IN2 IN3)
(REDUCE #'+ (LIST IN1 IN2 IN3))))
(LIST #'FUN-CREATED '(IN1 IN2 IN3)))
And is called in the following way :
(defparameter *fun-created2* (create-funtest2))
(funcall (car *fun-created2*) 1 2 3) ; => 6, OK
(second *fun-created2*) ; => (IN1 IN2 IN3), OK