5

Is it possible to disable eager load-time macro expansion in emacs 24.3.1? For debugging purposes, I would like to expand macros lazily on each invocation at run-time.

A working non-standard method to obtain lazy macro evaluation is to include a variable dummyVar (undefined at load-time, defined later at run-time), then an error message like "Eager macro-expansion failure: (void-variable dummyVar)" occurs at load-time and the macro is expanded at run-time. I did not find a lisp command to get this behaviour.

  • Couldn't you just eval them? I.e. store the code inside a function and when that function is invoked, eval the stored code with whatever arguments you need replaced? But, come to think of it, what kind of testing are you trying to do, if you are testing something that, generally speaking, shouldn't happen anyway? Why simply using macroexpand together with executing the resulting code is not enough / how delaying the evaluation would help? – wvxvw Oct 12 '15 at 20:09
  • The advantage of the macro is that the unevaluated expressions for the arguments are also available, as well as the evaluated expressions. These unevaluated expressions are needed for debugging purpose. I don't know how to obtain unevaluated expressions using eval. – Zacharias Oct 12 '15 at 21:44
  • Can you elaborate on debugging purposes? I'll try to come up with an example of using eval for this. – wvxvw Oct 12 '15 at 21:53
  • Good question. – Drew Oct 13 '15 at 2:32
  • Simple example: file test2.el: (load "setq2.el") (defun test-setq2()(setq2 "@id" x1 "x1Val")) (princ "before setq2\n") (test-setq2) (princ (format "after setq2 x1: %s\n" x1)) file setq2.el: (defmacro setq2 (infoString x y) (princ (format "setq2 %s (setq %s %s)\n" infoString x y)) (setq ,x ,y))` emacs -batch -l test2.el provides if eager macro evaluation: setq2 @id (setq x1 x1Val) before test-setq2 after test-setq2 x1: x1Val if lazy macro eval: (defun internal-macroexpand ...) before load: before test-setq2 setq2 @id (setq x1 x1Val) after test-setq2 x1: x1Val – Zacharias Oct 13 '15 at 19:09
3

Here's something that does what you want, however, I doubt that that is what you really need. This doesn't really help debugging, if anything, it makes debugging more complicated:

(defmacro lazy-macro (name args &rest body)
  (declare (indent defun))
  `(defun ,name ,args
     (eval '(progn ,@body))))

(lazy-macro lazy-test (foo)
  (message "%s" foo))

(lazy-test (list 1 2))
;; "(1 2)"

Note that '(progn ,@body) will by just a list at run-time, so, if you wanted to, you could inspect it / change it after the function is compiled.

3

Looking at the code (specifically the readevalloop function in lread.c) shows that the following prevents eager macroexpansion from being attempted:

(fmakunbound 'internal-macroexpand-for-load)

However it turns out that you can't actually put that in a library which causes other libraries to be loaded, as it makes the function unbound at a point after it has been established to exist, and consequently Emacs attempts to call it later on.

Zacharias' solution to this is to not unbind the function, but replace it with something which is effectively a no-op for this particular case: A function which returns the same form it is given, unmodified (i.e. something which Emacs thinks will perform macroexpansion, but which does no such thing).

(defun internal-macroexpand-for-load (form) form)

Alternatively, you can disable eager macroexpansion within a temporary local scope as follows; which means you can keep it enabled in general (for the usual performance benefits), but perform individual library loads (or any other evaluations) without eager expansion for the specific things you're interested in:

(require 'cl-lib)
(cl-letf (((symbol-function 'internal-macroexpand-for-load) nil))
  (fmakunbound 'internal-macroexpand-for-load)
  (load "YOUR-LIBRARY.el"))

(Obviously you need to be specific about .el in this scenario.)

n.b. this one does seem to work fine in general with that fmakunbound call (and relatedly, while 24.3 complained if that call was absent, Emacs 24.5 was seemingly happy to equate a symbol-function of nil with an unbound function, and appeared to work as desired with no fmakunbound call in the cl-letf body).

(You could, of course, use a function which returns its argument in this cl-letf version as well. I think the code above is fine as-is, but if you ever encountered an issue with it, that would be a sensible alternative to try.)

  • Working here in 24.3 and 24.5 if used before load: (defun internal-macroexpand-for-load (form) form ). (fmakunbound 'internal-macroexpand-for-load) generates error message: Symbol's function definition is void: internal-macroexpand-for-load – Zacharias Oct 13 '15 at 18:49
  • Indeed, probably best to (require 'macroexp) or eval-after-load the unbinding. I think the cl-letf method works regardless. – phils Oct 13 '15 at 19:33
  • Well, I can confirm that a plain fmakunbound simply doesn't work in the init file. I think perhaps it gets evaluated between the test for it existing and the point at which it's called. Your approach resolves that, so I'll update the answer accordingly. – phils Oct 14 '15 at 9:27
  • I just received an unexplained down-vote for this. Exciting! – phils Dec 19 '15 at 0:34
0

I think your macro is simply wrong. You probably want something more like:

(defmacro setq2 (infoString x y)
  `(progn
     (princ (format "setq2 %s (setq %s %s)\n" ,infoString ',x ',y))
     (setq ,x ,y)))

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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