cl-advice

2022-04-01

Portable advice for Common Lisp

Upstream URL

github.com/szos/cl-advice

Author

szos at posteo dot net

License

LGPL
README
CL-ADVICE

A lightweight and portable system for advising functions in Common Lisp.

1Description

CL-ADVICE implements a new function type which carries with it slots forbefore, around, and after advice. Functions can be defined as advisable,existing functions not within a locked package can be converted to advisablefunctions, and advisable functions can be converted to regular functions.

1.1Types of Advice

Pieces of advice are functions which get called before, after, or around themain function. Generally speaking, its a good idea to define advice functionsas named functions and add it as a symbol and not a function object. Thismakes removing advice easier, and allows the advised function to use the newdefinition should the advice function be recompiled. Additionally, whileinstalling anonymous functions as advice is allowed, removing anonymousfunction advice requires knowing where in the advice list it lies, or holdinga reference to the anonymous function object.

1.1.1Before and After Advice

Before and After advice must have an argument list that is compatible withthe main function. Two argument lists are considered compatible if they canboth be applied to the same arguments. These advice functions are loopedthrough and called in order.

1.1.2Around Advice

Around advice must take the next function to be called as its firstargument, and all following arguments must be compatible with the mainfunctions argument list. Around advice is unique in that it has control overwhether or not the next function will be called. The next function may bethe main function, or the next piece of around advice.

2Usage

This system is used primarily through the functions make-advisable,defun-advisable, add-advice, replace-advice, and remove-advice. Formore information, see the docstrings of the functions and macros exported inpackage.lisp.

2.1Redefining functions

The macro defun-advisable copies existing advice if and only if thefunction has the same argument list (as compared by equal).

2.2Example: TRACE

We can implement trace in terms of :around advice like so:
  (defpackage :tracer
    (:use :cl :cl-advice))

  (in-package :tracer)

  (defun make-simple-tracer (&optional (sym 'unknown-function))
    (let ((inc 0))
      (lambda (next-fn &rest args)
        (let ((string (make-string inc :initial-element #\space)))
          (format t "~&~A~A: Calling ~A with arguments ~A~%"
                  string inc sym args)
          (incf inc)
          (let ((result (apply next-fn args)))
            (decf inc)
            (format t "~&~A~A: ~A returned ~A~%"
                    string inc sym result)
            result)))))

  (defun-advisable fib (n)
    (if (< n 2)
        n
        (+ (fib (- n 1)) (fib (- n 2)))))

  (add-advice :around 'fib (make-simple-tracer 'fib))

The result of this is that when fib is called, the following will be printed to standard output:

  TRACER> (fib 1)
   0: Calling FIB with arguments (1)
   0: FIB returned 1
  1
  TRACER> (fib 5)
   0: Calling FIB with arguments (5)
    1: Calling FIB with arguments (4)
     2: Calling FIB with arguments (3)
      3: Calling FIB with arguments (2)
       4: Calling FIB with arguments (1)
       4: FIB returned 1
       4: Calling FIB with arguments (0)
       4: FIB returned 0
      3: FIB returned 1
      3: Calling FIB with arguments (1)
      3: FIB returned 1
     2: FIB returned 2
     2: Calling FIB with arguments (2)
      3: Calling FIB with arguments (1)
      3: FIB returned 1
      3: Calling FIB with arguments (0)
      3: FIB returned 0
     2: FIB returned 1
    1: FIB returned 3
    1: Calling FIB with arguments (3)
     2: Calling FIB with arguments (2)
      3: Calling FIB with arguments (1)
      3: FIB returned 1
      3: Calling FIB with arguments (0)
      3: FIB returned 0
     2: FIB returned 1
     2: Calling FIB with arguments (1)
     2: FIB returned 1
    1: FIB returned 2
   0: FIB returned 5
  5 (3 bits, #x5, #o5, #b101)

Dependencies (2)

  • closer-mop
  • fiveam

Dependents (0)

    • GitHub
    • Quicklisp