Reputation: 75
I tried to write a macro and execute it as follow. but it failed to execute.
(defmacro times_two (var) (* 2 var))
(times_two '(+ 1 2))
In my imagination, I think the expansion would be (* 2 (+ 1 2)). and after execution, the result would be 6. But failed.
I don't know why. I read the Emacs lisp manual, but I still can't understand them. I want to know what on earth the exact steps is while constructing expansion. What did the interpreter do?
Upvotes: 3
Views: 205
Reputation: 358
I suspect that you're confusing compile-time and run-time. Macros run at compile time, producing code to be executed at run-time. Generally speaking it's hard to keep these straight and it makes writing macros difficult.
In any event, when I put this into ielm, I get:
ELISP> (defmacro times_two (var)
(* 2 var))
times_two
ELISP> (times_two '(+ 1 2))
*** Eval error *** Wrong type argument: number-or-marker-p, (quote (+ 1 2))
ELISP>
At least part of the problem then is the '(+ 1 2), but then when I remove the quote the following is no better:
ELISP> (times_two (+ 1 2))
*** Eval error *** Wrong type argument: number-or-marker-p, (+ 1 2)
ELISP>
It seems that elisp is looking for a number or a marker in the place where we are putting '(+ 1 2) and (+ 1 2). Let's try using a number:
ELISP> (times_two 3)
6
That works.
Interestingly, the macro expansion of this gives:
ELISP> (macroexpand '(times_two 3))
6
Which is probably not really what we want.
When we write macros we want to return expressions to be evaluated at run time. So rather than returning a number we can try this:
ELISP> (defmacro times_two (var)
`(* 2 ,var))
The backtick (quasiquote) is a way of creating a list, but also allowing interpolation with the use of a comma. Defining times_two in this way gives:
ELISP> (times_two (+ 1 2))
6
And the expansion of:
ELISP> (macroexpand '(times_two (+ 1 2)))
(* 2
(+ 1 2))
Which is exactly how you imagined it.
Upvotes: 1
Reputation: 5942
When I evaluate these forms in Emacs, I get this error message when evaluating the second one:
Debugger entered--Lisp error: (wrong-type-argument number-or-marker-p (quote (+ 1 2)))
*(2 (quote (+ 1 2)))
(lambda (var) (* 2 var))((quote (+ 1 2)))
(times_two (quote (+ 1 2)))
eval((times_two (quote (+ 1 2))))
eval-last-sexp-1(nil)
eval-last-sexp(nil)
call-interactively(eval-last-sexp nil nil)
This is showing you how it expanded the macro, which should tell you what went wrong. (The final expansion is at the top.)
The quoted expression '(+ 1 2)
gets passed to the times_two macro, but a quoted list is not a valid argument to the *
function.
What you actually want here is:
(defmacro times_two (var) `(* 2 ,var))
(times_two (+ 1 2))
Keep in mind that, generally, the result of a macro will be new Lisp code, not a final value. Your goal in writing a macro is to construct the form that will give you the result you want. Thus, most of the time your macro will end up using the quasiquote (`) syntax.
Upvotes: 6