5

Can someone explain and show the right/preferred way of advising functions in Emacs these days.

Official documentation says to use advice-add, but I've seen people using define-advice and I checked, they are both part of nadvice.el (which is as I understood ships with Emacs).

I'm not sure, but If I remember it right I think there were more ways to advice (some of them are now considered obsolete).

Also it's known that using lambdas to add hooks is kinda frowned upon. Is that the same for advising? Then why something like this is okay (or is it?):

(define-advice mu4e~stop (:after nil kill-mu4e-layout-after-mu4e~stop)
        (when mu4e-spacemacs-kill-layout-on-exit
          (persp-kill mu4e-spacemacs-layout-name)))

It seems define-advice is a macro that uses advice-add behind the scenes, but I don't see how it makes it easier to advise functions, what am I missing?

This all is very confusing.

iLemming
  • 1,223
  • 9
  • 14

2 Answers2

1

I tried pp-macroexpand-last-sexp on define-advice block and it turns out that I was right, it is a macro that uses advice-add behind the scenes, but it implicitly creates a defun (since using lambdas for hooks and advising is a "no-no"), so you don't have to do it yourself.

It's confusing because frankly define-advice doesn't seem to reduce the code that needs to be written too much.

iLemming
  • 1,223
  • 9
  • 14
  • `define-advice` is a wrapper of `advice-add` and `defun`, it provides two benefits: 1) when you supply name, it will define a function name SYMBOL@NAME, so the prefix name convention is respected automatically. 2) The code is more compact since it is defun + advice-add. – xuchunyang Jul 08 '19 at 04:20
  • I haven't used nadvice much, but I guess the point of the `define-advice` form is partly that it's analogous to `defadvice`. (And that being the case, it could also eliminate the issue of nadvice defuns not being explicitly and obviously advice when you're browsing the code, in the way that defadvice was.) – phils Jul 08 '19 at 09:36
1

advice-add and define-advice are both presented in the Emacs Lisp manual (see "Advising Named Function" in the Emacs Lisp manual). Each of these functions probably has its own use although they are quite similar.

1. advice-add

(defun his-tracing-function (orig-fun &rest args)
  (message "display-buffer called with args %S" args)
  (let ((res (apply orig-fun args)))
    (message "display-buffer returned %S" res)
    res))

(advice-add 'display-buffer :around #'his-tracing-function)

This first form is in my opinion the least abstract. We first define a function and then add it as an advice to another function. In the same way, we can add or remove any piece of advice at any time.

2. define-advice

(define-advice display-buffer
    (:around (orig-fun &rest args) his-tracing-function)
  (message "display-buffer called with args %S" args)
  (let ((res (apply orig-fun args)))
    (message "display-buffer returned %S" res)
    res))

This second form is in my opinion the most homogeneous. The "chunck" of code will interact with the original function. It is interesting because it presents the advice combinator and the function arguments (see "Advice Combinators" in the Emacs Lisp manual), together they define the composed form.

Finally, the first form may be less obvious whereas the second form is specific and more compact.

  • It strikes me that to use define-advice you need to break your 'his-tracing-function into two - a pre apply and a post apply. I don't see any benefit to this. – RichieHH Apr 04 '21 at 19:29