Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 57

Function Modification with Advice

Recommended resources

Introduction

Welcome to the next lesson in our journey to extend Emacs. In our previous lesson, you learned how to use hooks to execute your own code in response to specific events, like a major mode starting. This is a powerful way to automate your environment. For example, you created a function to set up a comfortable writing environment and hooked it into org-mode.

Hooks are excellent for running code alongside Emacs's normal operations. But what if you need to alter the very behavior of an existing function? For instance, what if you wanted every file save command to also create a timestamped backup, or you wanted to change how Emacs splits windows? Redefining the entire original function is an option, but it's messy and difficult to maintain.

This lesson addresses that exact challenge, focusing on the learning outcome: Explain how advice modifies existing functions without altering their source code. We will explore "advice," a core Elisp concept for cleanly and modularly augmenting the functionality of any existing function. This is one of the most powerful customization tools in your Emacs toolkit.


1. What is Advice and Why Use It?

At its core, advice is a mechanism for "wrapping" an existing function with your own code. Instead of completely replacing a function, you add pieces of logic that run before, after, or even around the original function call.

This is a much cleaner approach than bluntly redefining a function. Let's start by reading the official documentation's introduction to this idea.

Advising Functions (GNU Emacs Lisp Reference Manual)

The GNU Emacs Lisp Reference Manual provides a concise explanation of why advice is a superior method for modifying functions.

Please read the first three paragraphs, starting from 'When you need to modify a function...' and ending with '...advising the function'. Pay close attention to the contrast between 'blunt' methods like defun and the 'cleaner method' of advice.

As the manual states, methods like redefining a function with defun are "too blunt" because they completely discard the original function. Using advice offers several key advantages:

  • Modularity: Your changes are kept separate from the original code. You don't need to copy and paste the original function's source into your configuration just to make a small tweak.
  • Maintainability: When you update Emacs or a package, the original function might be improved or fixed. If you've redefined it, you won't get those updates. Advice, however, applies your changes to the new version of the function automatically.
  • Reversibility: Advice can be easily added and removed programmatically. This makes it simple to turn your customizations on or off, which is much harder if you've completely redefined a function.

Given your background in creative direction, you can think of it like this: Advising a function is like giving stage directions to an actor. The original script (the function's source code) remains untouched. You are simply adding notes that modify the performance:

  • "Before you say your line, pause and look at the audience."
  • "After you say your line, exit stage left."
  • "Instead of saying your line, just perform this gesture."

This leaves the original work intact while allowing you to augment it in powerful ways.


2. The Anatomy of Advice

The modern advice system in Emacs, introduced in 2012, is centered around the advice-add function. Let's look at the basic components.

Some Emacs advice

The blog post 'Some Emacs advice' provides a good, high-level overview of the history and basic usage of advice.

Please read the sections 'Advising Emacs' and 'History'. Note the distinction between the old defadvice and the newer, recommended advice-add system. You don't need to worry about the code examples for now.

As the article explains, advising a function involves three key elements:

  1. The Target Function: The original function you want to modify (e.g., save-buffer).
  2. The Advising Function: The function you write that contains the new behavior.
  3. The Combinator: A keyword that specifies how your advising function should be combined with the target function.

The most common combinators are :before, :after, and :around. These keywords are the "stage directions" that tell Emacs when to run your code relative to the original function.

Emacs Lisp Function Advice Display
This image shows what Emacs reports when you inspect a function that has been advised. Notice how the single function `foo` now has multiple pieces of advice attached to it: a `:before` piece, an `:after` piece, and even an `:override`. This illustrates the modular nature of advice.

3. Types of Advice: A Conceptual Overview

Let's briefly define what the main combinators do. You will get hands-on experience with them in the next lesson, but for now, we'll focus on the concepts.

  • :before: Your advising function runs before the original function is called. It receives the same arguments as the original, but its return value is ignored. This is useful for setup tasks or checks that need to happen before the main action.
  • :after: Your advising function runs after the original function completes. This is useful for cleanup tasks or for actions that depend on the result of the original function.
  • :around: This is the most powerful combinator. Your advising function wraps the original function. It receives the original function itself as an argument, along with all the other arguments. Your code is now in complete control: it can decide whether to call the original function, what arguments to pass it, and what to do with its return value.

4. A Concrete Example: Tracing a Function

To see how this works in practice, let's look at a classic use case for advice: tracing the execution of a function to understand how it's being used. The display-buffer function, for example, is a complex part of Emacs responsible for deciding how and where new windows appear. Let's imagine we want to see exactly how it's being called.

The GNU Manual provides a perfect example of this using :around advice.

Advising Functions (GNU Emacs Lisp Reference Manual)

Let's examine a practical example from the GNU Emacs Lisp Reference Manual. This code adds tracing messages before and after calls to display-buffer.

Focus on the code block that starts with (defun his-tracing-function ...) and the text explaining it. We will break it down together below.

Let's dissect the code from the manual:

;; 1. Define the advising function
(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))

;; 2. Add the advice
(advice-add 'display-buffer :around #'his-tracing-function)

Here’s what’s happening, step by step:

  1. his-tracing-function: This is our custom advising function. Because it will be used with :around advice, its first argument, orig-fun, will be the original display-buffer function itself. The &rest args collects all of the original arguments into a list named args.
  2. (message "display-buffer called..."): This is our :before action. It prints a message showing us what arguments display-buffer was called with.
  3. (apply orig-fun args): This is the critical part. We are now calling the original function (orig-fun) that was passed to us, applying the original arguments (args) to it. We store its return value in the variable res.
  4. (message "display-buffer returned..."): This is our :after action. It prints a message showing what the original function returned.
  5. res: The advising function returns the original function's result, ensuring the overall behavior is preserved.
  6. advice-add: This call connects everything. It tells Emacs: "When display-buffer is called, run his-tracing-function around it."

With this advice active, every time anything in Emacs tries to display a new buffer, you will see log messages telling you exactly what is happening, all without ever touching the source code of display-buffer. To stop the tracing, you would simply call (advice-remove 'display-buffer #'his-tracing-function).


Conclusion

In this lesson, you have learned the concept of function advice in Emacs Lisp. You now understand that it provides a clean, modular, and maintainable way to modify the behavior of existing functions without directly altering their source code.

Key Takeaways:

  • Advice is a mechanism for wrapping existing functions with new code.
  • It is a cleaner and more robust alternative to function redefinition.
  • The modern system uses advice-add to connect an advising function to a target function.
  • Combinators like :before, :after, and :around specify how the new and old code should interact.
  • :around advice is the most powerful, giving your code full control over the execution of the original function.

Preview of the Next Lesson

You now have a solid conceptual understanding of what advice is and why it's so useful. In the next lesson, we will move from theory to practice. You will learn to use advice-add with the :before, :after, and :around combinators to solve practical customization problems, further tailoring Emacs to your specific workflow.