0

Have read that macro provide a way to perform transformations before the code is actually evaluated. When the macro is called, its arguments are passed to the macro as unevaluated forms. The macro then generates objects which are substituted and then evaluated in place of macro arguments.

Does this mean that macros are never mean to return a result as functions do ? And that the last expression in the body of the macro is never returned ? That thusly, one should not try to return anything as the last expression ?

The Lisp interpreter proceeds to evaluate the expansion as soon as it comes back from the macro. But, what does it mean that the value returned by the macro body is an alternate Lisp expression (expansion) ?.

Drew
  • 75,699
  • 9
  • 109
  • 225
Dilna
  • 1,173
  • 3
  • 10

2 Answers2

2

A macro is a function that returns code. The code returned by the macro is executed instead of the macro invocation. Suppose we have the following macro:

(defmacro do-the-thing (foo bar)
  `(+ ,(third bar) ,(second foo)))

If I evaluate the expression (do-the-thing (* 3 5) (* 5 7)). The expression '(* 3 5) will be assigned to foo, and '(* 5 7) to bar. The third element of bar and the second element of foo will be substituted into a list, and the macro will return '(+ 7 3) to be evaluated.

Strictly speaking, code is executed in multiple steps. First, all of the macros are expanded. This is a recursive process, as it is not uncommon for a macro to emit code that itself uses macros. All of the code returned by the macros is substituted into the place of the macro calls, so at the end of this step there are no more macro calls anywhere in the code.

Only then is the resulting code evaluated.

These two steps might happen in quick succession, for example if you type some code into the *scratch* buffer and then hit C-j. Or they might be separated by a long time, for example if you byte–compile a file. The file is loaded, all of the macros are expanded, then the byte compiler does it’s thing, and the result is written to a file on disk. Only later is that file loaded and the code within it run.

You can see macro expansion in action by calling macroexpand or macroexpand-1, which expand macros and then return the code instead of letting it be evaluated:

(macroexpand-1 '(do-the-thing (* 3 5) (* 5 7))
(+ 7 3)

You should read chapter 14 of the Emacs Lisp manual which goes into detail about all of this.

db48x
  • 15,741
  • 1
  • 19
  • 23
  • So that is the return, the evaluation of the constructed code `(+ 7 3)`. So it is last expression and is returned after evaluation. In this sense, the macro behaves just like a function because calling `(do-the-thing (* 3 5) (* 5 7)` gives `10` as the macro return. Just as might happen with a function `(dodo 7 3)`. – Dilna Aug 05 '23 at 16:37
  • This macro is not an amazing example, but notice that `(second bar)` is able to retrieve the 7 because the expression had not yet been evaluated. If `do-the-thing` had been a function instead, then the arguments would have been evaluated first and `bar` would have been 35. Calling `second` on that would have generated an error. Then, notice that the macro returns not 10 but the code `'(+ 7 3)`. This is a list, just as much as if I had written `(list '+ 7 3)`. Once the macro returns this list, the evaluator inserts it back into the code where it will eventually be evaluated. – db48x Aug 05 '23 at 16:50
  • If it was a function `bar` would have been `35` - Agreed. You say the macro returns unevaluated code as `'(+ 7 3)`. And `macroexpand` evaluates the unevaluated code output. Does this mean that the interpreter evaluates the code expression after the code expression is returned by a macro ? – Dilna Aug 05 '23 at 17:23
  • `macroexpand` does _not_ evaluate the return value of the macro. It just returns it so that it can be printed out. If you had evaluated it instead, the evaluator would have evaluated it and returned the 10 to you. – db48x Aug 05 '23 at 17:38
  • The documentation for `macroexpand` states that it returns the result of expanding macros. The macro is expanded and the expansion is considered as FORM. – Dilna Aug 05 '23 at 18:18
  • I would conclude that the evaluation happens at a later time after calling the macro, for instance, when a macro is called as an argument to a function. – Dilna Aug 05 '23 at 18:25
  • I can output the code as a string with `(message "%s" '(do-the-thing (* 3 5) (* 5 7)))` and `(message "%s" (macroexpand '(do-the-thing (* 3 5) (* 5 7))))`. Although what is returned by the macro is not a string. – Dilna Aug 05 '23 at 19:09
  • Right. When you run some code, such as by loading a file or executing it in the `*scratch*` buffer, first all of the macros are expanded, and then the result is evaluated. See [chapter 14.2 Expansion of a Macro Call](https://www.gnu.org/software/emacs/manual/html_node/elisp/Expansion.html) of the Elisp manual. As for `message`, what it does is turn normal lisp objects into strings. If you go to the `*scratch*` buffer you can execute the code and see the result directly, no need to use `message`. – db48x Aug 05 '23 at 20:51
  • 1
    Please remember that comments are *not* for conversation, but only for clarification of the question or answer provided. Add-on questions should instead become new questions. – Trevoke Aug 05 '23 at 20:57
2

Since there's a lot in your comments, and comments can be deleted and aren't searchable, I'll add this info as an answer. As @db48x said, the macro call is expanded to produce some code, and that code is then evaluated.

Macro expansion is pure reduction, just like what pure functional programming does. The macro arguments are not evaluated, to start with, and some or all of them might never be evaluated during the macro call - they might even be ignored altogether.

Macro expansion need not really expand anything. What it does, using Lisp code, is produce some Lisp code.

After macro expansion, as the second step of evaluating a macro call, the code produced by the first step (macro expansion) is evaluated, and the result of that evaluation is returned.

In sum, Lisp sees a macro call, and ends up evaluating some code and returning its value. What code does it evaluate? The code generated by the macro definition - during the step that's called macro "expansion".

The macro definition, i.e., the code in the macro that generates code, doesn't evaluate that code. The Lisp interpreter sees a macro call, and to evaluate/interpret it, it uses the macro's code to perform macro expansion, and then the interpreter evaluates the result of that expansion.

Typically, the code in the macro-defining body that generates the code to evaluate does something with one or more of the arguments passed in the call. Typically, the arguments determine what code gets generated, in some way (e.g. condition tests).

The macro code that generates the code to be evaluated can, but it doesn't have to, evaluate one or more of the arguments.

Generally speaking, macro code doesn't perform side effects. If you're writing macro code that does that, then you're likely doing the wrong thing.

The only purpose of the macro code is to create the Lisp code that will be evaluated - nothing more. To do that, it can do anything it needs/wants to do, using anything Lisp provides.

Drew
  • 75,699
  • 9
  • 109
  • 225
  • In summary, a macro produces lisp code. But when saying that the code will be evaluated, it does not mean that the macro evaluates it. It will only be evaluated in some context that the interpreter determines to evaluate the code. – Dilna Aug 05 '23 at 20:59
  • Please reread what I wrote. The macro definition, the code in the macro that generates code, doesn't evaluate that code. The Lisp interpreter sees a **macro call**, and to evaluate/interpret it, it uses the macro code to perform macro expansion, *and then it evaluates* the result of that expansion. – Drew Aug 05 '23 at 21:01