Skip to content

LISP - + Method Combination

Modernizing CLOS: The + Method Combination

Section titled “Modernizing CLOS: The + Method Combination”

The Common Lisp Object System (CLOS) offers a powerful and unique feature called Method Combination. It allows you to define how multiple methods, all applicable to a single generic function call, should be combined to produce a final result. This tutorial explores the + method combination, a built-in technique for summing the results of applicable methods, which is perfect for tasks like aggregation and scoring.

Core Concept: Defining a Generic Function with + Combination

Section titled “Core Concept: Defining a Generic Function with + Combination”

To use the + method combination, you specify it when defining a generic function using defgeneric. All primary methods defined for this generic function are then expected to return a number. CLOS will automatically sum these numbers to produce the final result.

(defgeneric calculate-cost (x y)
(:documentation "Calculates a total cost by summing results from specialized methods.")
(:method-combination + :most-specific-first))
;; Define a primary method for two numbers. This is one of the methods to be combined.
(defmethod calculate-cost ((x number) (y number))
"The base cost is the sum of two numbers."
(+ x y))
  • defgeneric: This is where the combination strategy is declared. (defgeneric calculate-cost (x y) ...) defines the function’s signature.
  • :method-combination +: This key part tells CLOS to use the summing combination. When calculate-cost is called, CLOS finds all applicable primary methods, calls them, and adds their numeric results together.
  • defmethod: This defines a specific implementation for certain parameter types (classes). Notice the method definition itself does not include the + symbol; it just returns a number. The combination logic is handled by the generic function.
  • Extensibility: The power of this approach is that you can add new methods for different types without changing the original code. For example, we can define how to calculate costs involving strings by considering their length.

Practical Example: A Multi-Type Cost Calculator

Section titled “Practical Example: A Multi-Type Cost Calculator”

Let’s build a complete example that calculates a ‘cost’ by adding numbers directly or by using the length of strings. This demonstrates how CLOS dispatches to the correct methods and how + combination aggregates the results.

;; Modern Lisp projects are managed with systems like ASDF and Quicklisp.
;; This code can be part of a system defined in a .asd file.
(defpackage #:cost-calculator
(:use #:cl)
(:export #:calculate-cost))
(in-package #:cost-calculator)
;; 1. Define the generic function with the '+' method combination.
(defgeneric calculate-cost (x y)
(:documentation "Calculates a total cost by summing results from specialized methods.")
(:method-combination + :most-specific-first))
;; 2. Define methods for different type combinations.
;; Each method is a 'primary' method and must return a number.
(defmethod calculate-cost ((x number) (y number))
"Adds two numbers."
(+ x y))
(defmethod calculate-cost ((x number) (y string))
"Adds a number to the length of a string."
(+ x (length y)))
(defmethod calculate-cost ((x string) (y number))
"Adds the length of a string to a number."
(+ (length x) y))
(defmethod calculate-cost ((x string) (y string))
"Adds the lengths of two strings."
(+ (length x) (length y)))
;; 3. Demonstrate the usage.
(defun run-examples ()
(format t "~&Cost of 10 and 5: ~d" (calculate-cost 10 5))
(format t "~&Cost of 10 and \"test\": ~d" (calculate-cost 10 "test")) ; "test" has length 4
(format t "~&Cost of \"alpha\" and 20: ~d" (calculate-cost "alpha" 20)) ; "alpha" has length 5
(format t "~&Cost of \"hello\" and \"world\": ~d" (calculate-cost "hello" "world")))
;; To run this:
;; (cost-calculator:run-examples)
Cost of 10 and 5: 15
Cost of 10 and "test": 14
Cost of "alpha" and 20: 25
Cost of "hello" and "world": 10

When (calculate-cost 10 "test") is called, CLOS identifies that the (number string) method is the most specific match. It calls this single method. The + combination simply returns the result since there is only one applicable method. The real power shines when you have multiple methods contributing, such as from inheritance or multi-methods that are not mutually exclusive.

  • Type Safety: All primary methods for a + combined generic function must return a number. Returning nil or any other type will signal a TYPE-ERROR at runtime.
  • Readability: Use + combination when the act of summing results is intuitive and clear. For more complex logic, a standard method with :around qualifiers might be more maintainable.
  • Zero Contribution: If a method should contribute nothing to the sum under certain conditions, it should explicitly return 0.
  • Other Combinations: Explore other built-in combinations like and, or, list, append, and max to see the full power of this CLOS feature. They follow the same principle but perform different operations on the results.