Skip to content

LISP - :primary Method Combination

In the Common Lisp Object System (CLOS), method combination defines the process by which all applicable methods for a generic function call are combined into one effective method. The Standard Method Combination is the default and most commonly used type. It orchestrates the execution of methods based on their roles, which are specified by qualifiers.

The most fundamental type of method is a primary method. These are methods defined without any qualifiers. In the standard combination, CLOS calls the single most specific primary method. This method is responsible for providing the main logic and returning the final value of the generic function call.

  • Generic Function (defgeneric): Defines a function’s name and parameter list, acting as a dispatcher. It can also specify the method combination type, but standard is the default.
  • Method (defmethod): Provides a concrete implementation for a generic function for specific parameter types (classes).
  • Primary Method: A method with no qualifier. It forms the core logic. In the default standard combination, only the most specific one is executed.
  • Auxiliary Methods: Methods with qualifiers like :before, :after, and :around. They augment the behavior of the primary method. This tutorial focuses only on primary methods.

Defining a Generic Function and Primary Methods

Section titled “Defining a Generic Function and Primary Methods”

First, we define a generic function. Then, we provide different method implementations for different data types.

;; Define a generic function named 'combine' that accepts two arguments.
;; By default, it uses the standard method combination.
(defgeneric combine (x y))
;; Define a primary method for the 'combine' generic function.
;; This method specializes on 'x' and 'y' both being of type 'number'.
(defmethod combine ((x number) (y number))
"This primary method handles the case where both arguments are numbers."
(+ x y))
;; Define another primary method for 'string' types.
(defmethod combine ((x string) (y string))
"This primary method handles the case where both arguments are strings."
(concatenate 'string x y))

When combine is called, CLOS inspects the types of the arguments and dispatches the call to the most specific matching method.

Let’s create a complete, runnable example that demonstrates how different primary methods are invoked based on argument types.

;; Modern Lisp development often starts with defining a package.
(defpackage #:method-combination-example
(:use #:cl))
(in-package #:method-combination-example)
;; 1. Define a generic function.
(defgeneric combine (x y)
(:documentation "Combines two objects based on their type."))
;; 2. Define a primary method for numbers.
(defmethod combine ((x number) (y number))
"When combining two numbers, return their sum."
(+ x y))
;; 3. Define a primary method for strings.
(defmethod combine ((x string) (y string))
"When combining two strings, return their concatenation."
(concatenate 'string x y))
;; 4. Define a primary method for lists.
(defmethod combine ((x list) (y list))
"When combining two lists, append them."
(append x y))
;; --- Demonstrate the dispatch ---
(format t "Combining numbers 10 and 20: ~a~%" (combine 10 20))
(format t "Combining strings \"Hello\" and \" World!\": ~a~%" (combine "Hello" " World!"))
(format t "Combining lists '(1 2) and '(3 4): ~a~%" (combine '(1 2) '(3 4)))
Combining numbers 10 and 20: 30
Combining strings "Hello" and " World!": Hello World!
Combining lists '(1 2) and '(3 4): (1 2 3 4)

When the generic function combine is called, the Common Lisp runtime examines the types of the arguments provided and selects the most specific primary method to execute:

  • (combine 10 20): Both arguments are numbers. The number-specialized method is invoked, returning their sum, 30.
  • (combine "Hello" " World!"): Both arguments are strings. The string-specialized method is invoked, returning the concatenated string, "Hello World!".
  • (combine '(1 2) '(3 4)): Both arguments are lists. The list-specialized method is invoked, returning the appended list, (1 2 3 4).

While primary methods are the workhorses, the true power of method combination comes from using auxiliary methods (:before, :after, :around) to create sophisticated, layered behavior. For instance, you could add an :around method to log every call to combine regardless of the argument types.