Reputation: 728
I'm trying to create a lisp macro that has an &optional
parameter with a default value. Unfortunately the parameter is processed differently depending on whether it is read from the default value or from parameters provided to the macro. The code fragment below reproduces the issue:
(setf table1 '((1 2 3)
(4 5 6))
table2 '((10 20 30)
(40 50 60)))
(defmacro test-lambda (f &optional (tableau table1))
`(list ,f ,tableau))
? (test-lambda 0 table2) ;; This works...
(0 ((10 20 30) (40 50 60)))
? (test-lambda 0) ;; ...but this doesn't
> Error: Car of ((1 2 3) (4 5 6)) is not a function name or lambda-expression.
> While executing: CCL::CHEAP-EVAL-IN-ENVIRONMENT, in process listener(1).
> Type :POP to abort, :R for a list of available restarts.
> Type :? for other options.
1 >
I don't quite understand why the macro won't work with the default value in the second case. Is there a better way to code this or at least a workaround?
Thanks,
Upvotes: 3
Views: 308
Reputation: 60004
You need to quote the default argument value:
(defmacro test-lambda-1 (f &optional (tableau 'table1))
`(list ,f ,tableau))
(test-lambda-1 0)
==> (0 ((1 2 3) (4 5 6)))
You need to think how Common Lisp evaluates your code: when it sees (test-lambda ...)
, it
Let us try it:
(macroexpand '(test-lambda 0))
==> (LIST 0 ((1 2 3) (4 5 6))) ; T
(macroexpand '(test-lambda-1 0))
==> (LIST 0 TABLE1) ; T
(macroexpand '(test-lambda 0 table2))
==> (LIST 0 TABLE2) ; T
(macroexpand '(test-lambda-1 0 table2))
==> (LIST 0 TABLE2) ; T
Now you can see where the error is coming from: you did not quote the default value of the argument, so it got evaluated twice.
Upvotes: 7