5
votes

Is there a way to do something like lexical closures using macrolet? What I want to do is make the following macro a local recursive helper that calls a function on each combination instead of generating a list as it does now calling the macro in the repl results in:

CL-USER> (combinations nil '(1 2 3) '(4 5 6))
((1 4) (1 5) (1 6) (2 4) (2 5) (2 6) (3 4) (3 5) (3 6))

What I would like is a macro that takes a function and any number of lists and results in nested loops that call the function on each combination. I'm pretty new to lisp, this is the first macro I've written beyond 'nif' clones and the like so any suggestions are appreciated.

I've tried to turn the macro into a macrolet in a macro that takes a function and the line '(nreverse (list ,item ,@vars))' is replaced with '(func (nreverse (list ,item ,@vars)))' but I get errors saying that func is an undefined variable or function.

This is the original function:

(defmacro combinations (vars &rest lsts)
  (with-gensyms (item)
    `(loop for ,item in ,(car lsts) ,(if (null (cdr lsts)) 'collecting 'nconcing)
       ,(if (null (cdr lsts))
            `(nreverse (list ,item ,@vars))
            `(combinations (,item ,@vars) ,@(cdr lsts))))))

This is what I've tried with macrolet and get undefined function 'func' errors.

(defmacro for-all-combonations (func &rest lst)
       (macrolet ((for-all (vars &rest lsts)
                    (with-gensyms (item)
                      `(loop for ,item in ,(car lsts) ,(if (null (cdr lsts)) 
                                                           'collecting 'nconcing)
                            ,(if (null (cdr lsts))
                                 `(func (nreverse (list ,item ,@vars)))
                                 `(for-all (,item ,@vars) ,@(cdr lsts)))))))
         (for-all nil lst)))
1
can you explain why you want to use macros for this? What should (let ((a '((1 2 3) (4 5 6)))) (combinations nil a)) do?Rainer Joswig
It should return '(((1 2 3)) ((4 5 6))) which is a list of all combination's taking an element from each list of input per combination. I'm doing this with a macro because that's what I've been exploring right now. I could do it without macros but I wanted to try my hand at writing a macro more complex than nif. This gives me a chance to find out what can and can't be done, I would never have wondered if a macrolet can be used in this way otherwise. Hopefully once I finish this I'll understand macros that much better.asm
I don't think this makes sense to do with a macro. Macros are there to compute source code, not data. If you want to compute data, the data needs to be there at macro expansion time - which it usual isn't.Rainer Joswig

1 Answers

5
votes

Macros are not first-class objects in Common Lisp, so you can't really have the equivalent of a lexical closure as a macro. You could get a similar effect by creating a function that generates a list that is a valid Lisp program, and then evals it.

That probably isn't a very good solution to your problem though. As Rainer Joswig said, macros are for manipulating source code. Use them when you want a new syntactic form that isn't built in to the language. Don't use them where you can write what you want with ordinary functions.