4

I think I found a bug in defmacro so I'd like to ask for confirmation before I make a fool of myself reporting something silly!

Here is how to reproduce it:

  1. Define the following macro which simply reports the value of case-fold-search in the minibuffer:
      (defmacro report-case-fold-search ()
        (let ((cfs case-fold-search))
          `(message "%s" ,cfs)))
  1. Execute it several times with randomly alternating values of case-fold-search:
      (progn (setq case-fold-search nil) (report-case-fold-search))
      (progn (setq case-fold-search t) (report-case-fold-search))

The output I get seems to indicate that there is a delay in the sense that each time I go from one to the other, it takes two tries to get the expected output.

  1. To be more precise, execute the following at least 3 times in a row:
      (progn (setq case-fold-search nil) (report-case-fold-search))
  1. Then execute once:
      (progn (setq case-fold-search t) (report-case-fold-search))
  1. This time a get nil for an answer even though case-fold-search has just been set to t.

Question. Can anyone reproduce this? If so, is there a sensible explanation for it? Otherwise, should I report it as a bug?

Here is my emacs version:

GNU Emacs 28.2 (build 2, x86_64-pc-linux-gnu, GTK+ Version 3.24.20, cairo version 1.16.0) of 2022-12-08

1
  • 3
    In most cases you would like to use defun, it defines a normal function. defmacro on the other hand might look the same but returns a replacement expression, and it's a lot harder to get right. However, if you really want to use a macro I would recommend the macrostep package which lets you see what a macro would expand to in the context where it is used. Jan 12 at 7:24

1 Answer 1

11

There's no bug.

Macros are normally expanded ahead of time (typically during byte-compilation or loading, but "not at eval-time" is the key assumption that you need to make), so any code you're evaluating as part of their expansion gets evaluated at whatever indeterminate time that expansion happens. For code which is evaluated interactively, the macro expansion does happen at that time, but it's still before the eval phase: firstly the code is read, then all macros in the code are expanded until none remain, and finally the result is evaluated.

In your case, the let binding happens only at expansion time, with the resulting value (of whatever case-fold-search was when expansion took place) being hard-coded into the expanded code that actually gets evaluated in place of the macro call.

If you were to repeatedly evaluate your progn interactively then macro expansion would happen each time, but it would always be the previous value of case-fold-search which was hard-coded for evaluation, because the setq doesn't happen until later, during the eval phase.

If you wanted the expanded code to take the eval-time value of case-fold-search into account, you would write this:

(defmacro report-case-fold-search ()
  `(let ((cfs case-fold-search))
     (message "%s" cfs)))

Although if that's a real example, you would just use:

(defmacro report-case-fold-search ()
  `(message "%s" case-fold-search))

There's no particular reason for it to be a macro at all, though, so a function would be preferable.

You will see some macros processing values at expansion time, but in such cases the values will usually be (only) the unevaluated arguments to the macro. Dealing with anything besides those arguments at expansion time is quite likely to be a bug.

9
  • 1
    I think you have an extra comma in the first example. Jan 12 at 7:22
  • 1
    Indeed; thank you @Lindydancer. Fixed now.
    – phils
    Jan 12 at 10:56
  • Thanks for your answer. That makes me very curious about the precise time the expansion is made. It seems hard to believe it takes place before the setting of case-fold-search.
    – Ruy
    Jan 12 at 12:56
  • 1
    I've updated the initial paragraphs to cover interactive evaluations.
    – phils
    Jan 12 at 23:27
  • 1
    And to further emphasise that last comment... when it comes to byte-compiled libraries, the macros were likely to have been expanded during some previous Emacs session rather than the current one (and/or in a separate asynchronous session if native-compilation is happening); and if you installed Emacs from an OS package manager then the macros for the core code were expanded on someone else's computer!
    – phils
    Jan 13 at 2:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.