Paulo Mendes
Paulo Mendes

Reputation: 728

&optional parameter in lisp macros: Why does this variable behave like this?

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

Answers (1)

sds
sds

Reputation: 60004

What

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)))

Why

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

Related Questions