In aspect and functional programming, advice describes a class of functions which modify other functions when the latter are run; it is a certain function, method or procedure that is to be applied at a given join point of a program.
The practical use of advice functions is generally to modify or otherwise extend the behavior of functions which cannot or should not be readily modified or extended. For instance, the Emacspeak Emacs-addon makes extensive use of advice: it must modify thousands of existing Emacs modules and functions such that it can produce audio output for the blind corresponding to the visual presentation, but it would be infeasible to copy all of them and redefine them to produce audio output in addition to their normal outputs; so, the Emacspeak programmers define advice functions which run before and after.
For a simple Emacs example: suppose after a user corrected a mis-spelled word using the Emacs ispell module, they wanted to re-spellcheck the entire buffer.
ispell-word offers no such functionality, even if the corrected word appears frequently in the buffer. The user could track down the definition of
ispell-word, copy it into their personal Emacs files, and add the desired functionality there, but this is tedious and, worse, brittle (the user's version is now out of sync with the core Emacs implementation, if it even works without further refactoring). What the user wants is quite simple — just to run another command after
ispell-word runs. Using advice, it can be done as simply as this:
(advice-add #'ispell-word :after #'flyspell-buffer)
A form of advices were part of C with Classes in the late 1970s and early 1980s, namely functions called
return defined in a class, which were called before (respectively, after) member functions of the class. However, these were dropped from C++.
Common Lisp implementations provide advice functionality (in addition to the standard method combination for CLOS) as extensions. LispWorks supports advising functions, macros and CLOS methods.
EmacsLisp added advice-related code in version 19.28, 1994.
The term "advice" goes back to the term advising as introduced by Warren Teitelman in his PhD thesis in 1966. Here is a quote from Chapter 3 of his thesis:
- Advising is the basic innovation in the model, and in the PILOT system. Advising consists of inserting new procedures at any or all of the entry or exit points to a particular procedure (or class of procedures). The procedures inserted are called "advice procedures" or simply "advice".
- Since each piece of advice is itself a procedure, it has its own entries and exits. In particular, this means that the execution of advice can cause the procedure that it modifies to be bypassed completely, e.g., by specifying as an exit from the advice one of the exits from the original procedure; or the advice may change essential variables and continue with the computation so that the original procedure is executed, but with modified variables. Finally, the advice may not alter the execution or affect the original procedure at all, e.g., it may merely perform some additional computation such as printing a message or recording history. Since advice can be conditional, the decision as to what is to be done can depend on the results of the computation up to that point.
- The principal advantage of advising is that the user need not be concerned about the details of the actual changes in his program, nor the internal representation of advice. He can treat the procedure to be advised _as a unit_, a single block, and make changes to it without concern for the particulars of this block. This may be contrasted with editing in which the programmer must be cognizant of the internal structure of the procedure.
Gregor Kiczales comments the above as follows:
- Advice appeared separately from Flavors in Maclisp and the Lisp Machine. You could advise any function, just like in Interlisp at the time. The before/after ontology appeared separately in Flavors methods.
- Method combination and macros were only marginally related until much later, in New Flavors and CLOS, when a macro-like mechanism was provided to allow people to define their own rules for combining methods. Prior to that the rules governing combination of before/after methods and so-called whoppers methods (around) was fixed, and the compiler just generated the code for that. There were things called wrappers, which had macro-like behavior, but I forget when they came around. Traipsing through the various versions of MacLisp and Lispm manual to get this part of the history exactly right could be interesting. Or it could be that Howard Cannon or David Moon or someone could actually remember it all exactly.
- The Design and Evolution of C++, p. 57
- "A Brief Guide to CLOS". Archived from the original on 2015-05-06. Retrieved 2015-04-27.
- LispWorks 7 User Guide and Reference Manual, The Advice Facility
- See, for example, AIM-602 at https://web.archive.org/web/20060913001624/http://www.ai.mit.edu/research/publications/browse/0600browse.shtml
- See AIM-57 at https://web.archive.org/web/20060913001624/http://www.ai.mit.edu/research/publications/browse/0000browse.shtml