Skip to content

LISP - and Method Combination

The Common Lisp Object System (CLOS) is one of Lisp’s most powerful features. It provides a flexible object-oriented paradigm that differs from systems like Java or C++. A key concept in CLOS is the generic function, which dispatches to specific methods based on the type (or class) of its arguments.

Method combination is the mechanism that defines how the results of all applicable methods for a given generic function call are combined to produce a single, final value. The default behavior is standard method combination, but Lisp provides several other built-in combination types, such as and.

The and method combination is useful for validation scenarios where multiple independent checks must all pass. It works as follows:

  • It calls all applicable primary methods, one by one, from most specific to least specific.
  • If any method returns nil, it immediately stops and returns nil.
  • If all methods are called and none return nil, it returns the non-nil value from the last method called (the least specific one).

Example: A Multi-faceted Validation System

Section titled “Example: A Multi-faceted Validation System”

Imagine we need to validate incoming data requests. A request is valid only if all its components are valid. This is a perfect use case for and method combination.

;; main.lisp
;; 1. Define a generic function with the 'and' method combination.
;; This function will check if an object is valid.
(defgeneric is-valid-p (object)
(:documentation "Checks if an object is valid. All checks must pass.")
(:method-combination and))
;; 2. Define methods for different types. Each method performs a specific check.
;; The most general check: by default, anything is valid unless specified otherwise.
;; This method will be called last and provides the final 't' if all others pass.
(defmethod is-valid-p and (object)
(declare (ignore object))
t) ; Default to true
;; A check for numbers: must be positive.
(defmethod is-valid-p and ((n number))
(> n 0))
;; A check for strings: must not be empty.
(defmethod is-valid-p and ((s string))
(> (length s) 0))
;; 3. Test the generic function with different inputs.
;; --- Test Cases ---
;; A positive number is valid.
(format t "Is 10 valid? ~a~%" (is-valid-p 10)) ; -> T
;; A negative number is not valid. The number method returns nil, short-circuiting.
(format t "Is -5 valid? ~a~%" (is-valid-p -5)) ; -> NIL
;; A non-empty string is valid.
(format t "Is \"hello\" valid? ~a~%" (is-valid-p "hello")) ; -> T
;; An empty string is not valid.
(format t "Is \"\" valid? ~a~%" (is-valid-p "")) ; -> NIL
;; A list is valid because it falls through to the most general 'object' method.
(format t "Is '(1 2) valid? ~a~%" (is-valid-p '(1 2))) ; -> T
Is 10 valid? T
Is -5 valid? NIL
Is "hello" valid? T
Is "" valid? NIL
Is '(1 2) valid? T

In this example:

  • We define is-valid-p as a generic function that uses the and combination.
  • When we call (is-valid-p 10), CLOS finds two applicable methods: the one specialized for number and the one for object. The number method is more specific, so it runs first. It returns t.
  • Since it didn’t return nil, the and combination proceeds to the next method, the one for object, which also returns t. This is the last value, so the final result is t.
  • When we call (is-valid-p -5), the number method runs and returns nil. The and combination immediately stops and returns nil without ever calling the object method.

Other Method Combinations and Further Learning

Section titled “Other Method Combinations and Further Learning”

Common Lisp provides other useful short-form method combinations like or (returns the first non-nil value), + (sums the results), append (appends list results), min, and max.

For more complex scenarios, you can use standard method combination, which allows you to define primary methods as well as auxiliary methods using qualifiers like :before, :after, and :around. This gives you fine-grained control over method execution and is a cornerstone of advanced Lisp programming.