Skip to content

LISP - append Method Combination

Method combination is a cornerstone of the Common Lisp Object System (CLOS), allowing developers to define how methods from different classes in a hierarchy should work together. While standard method combination (:around, :before, :after) is the most common, CLOS provides other built-in types for specialized tasks. This tutorial focuses on the append method combination, used for collecting results from multiple methods into a single list.

When you specify :method-combination append for a generic function, CLOS changes how it executes applicable methods. Instead of the standard hierarchy, it does the following:

  1. It calls all applicable primary methods for a given set of arguments.
  2. It expects each method to return a list.
  3. It concatenates all these lists together using the append function to produce the final result.

This is extremely useful for scenarios where different parts of your system need to contribute items to a collection, such as gathering permissions, features, or configuration values.

Practical Example: Gathering User Permissions

Section titled “Practical Example: Gathering User Permissions”

Let’s model a system where a user can have multiple roles, and each role grants a set of permissions. We want a function get-permissions that returns a complete list of all permissions for a user.

(defclass user ()
())
(defclass guest (user)
())
(defclass editor (user)
())
(defclass administrator (editor) ; An admin is also an editor
())

Step 2: Define the Generic Function with append

Section titled “Step 2: Define the Generic Function with append”
;; Define a generic function that will use the 'append' method combination.
(defgeneric get-permissions (user)
(:method-combination append))

Each method is specialized for a user class and returns a list of permissions associated with that role. Note the append keyword after the function name in defmethod.

;; Every user, even a guest, can view content.
(defmethod get-permissions append ((user guest))
(list :view-content))
;; An editor can also create and edit posts.
(defmethod get-permissions append ((user editor))
(list :create-post :edit-post))
;; An administrator has the highest level of permissions.
(defmethod get-permissions append ((user administrator))
(list :delete-user :manage-system))

Because of multiple inheritance and method combination, calling get-permissions on an instance of a class will trigger all relevant methods in its hierarchy, and append will collect the results.

;; Create instances of our user types
(let ((guest-user (make-instance 'guest))
(editor-user (make-instance 'editor))
(admin-user (make-instance 'administrator)))
(format t "Guest permissions: ~s~%" (get-permissions guest-user))
(format t "Editor permissions: ~s~%" (get-permissions editor-user))
(format t "Admin permissions: ~s~%" (get-permissions admin-user)))
Guest permissions: (:VIEW-CONTENT)
Editor permissions: (:CREATE-POST :EDIT-POST :VIEW-CONTENT)
Admin permissions: (:DELETE-USER :MANAGE-SYSTEM :CREATE-POST :EDIT-POST :VIEW-CONTENT)

The output for the administrator demonstrates the power of this technique. An administrator inherits from editor, which inherits from user (and implicitly guest in our setup). CLOS calls the methods for all three classes, and the append combination neatly gathers all the permissions into one comprehensive list. The order of results depends on the Class Precedence List (CPL), which by default places the most specific class’s results first.

CLOS offers other useful method combinations like + (sums numeric results), and (short-circuits on nil), or (short-circuits on non-nil), and list (collects results into a list of lists). Choosing the right combination type can greatly simplify your code.