Question

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?

Was it helpful?

Solution

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.

OTHER TIPS

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top