📝Common Lisp: Multiple Dispatch

Common Lisp § Multiple dispatch

Primary methods

;; Define the generic function first.
(defgeneric draw (shape)
  (:documentation "docstring."))

;; Next, define methods (i.e., generic function implementations)
(defmethod draw ((shape circle))
(defmethod draw ((shape triangle))

;; If there are multiple matching, a method can call
;; `call-next-method' to execute the next implementation.
(defmethod withdraw ((account checking-account) amount)
  ...some code...

Auxiliary methods

Common Lisp has primary and auxiliary methods. Auxiliary methods can be defined with :before, :after, and :around specifiers.

(defmethod withdraw :before ((account bank-account) amount) ...)
(defmethod withdraw :after ((account bank-account) amount) ...)
(defmethod withdraw :around ((account bank-account) amount) ...)

Execution order:

  1. All around methods in the order from most specific to less specific. Around methods should call call-next-method.
  2. When the least specific around method calls call-next-method, that executes:

    1. All before methods in the order from most specific to least specific.
    2. Primary method.

      1. Primary method may call call-next-method to execute the next less specific primary method.
    3. All after methods in the order from least specific to most specific (reverse order of before).

Method combinations

Be default, primary methods completely override less specific primary methods (although they can be executed with call-next-method). Common Lisp also has nine other combinations: +, and, or, list, append, nconc, min, max, and progn—they wrap all primary methods in the specified function/macro/operator. (New method combinations can be added with define-method-combination.)

If method combination is used, only primary and :around methods are supported.

(defgeneric priority (job)
  (:method-combination +))
;; :most-specific-last can be added to reverse the order of primary
;; methods (does not affect :around methods)

;; `defmethod' must also specify the method combination.
(defmethod priority + ((job express-job))

eql specializer

It is possible to create methods that specialize on a particular object with an eql specializer.

(defmethod withdraw ((account (eql *account-of-bank-president*)) amount)

The object in eql specializer is evaluated once only (i.e., if the value of that variable changes, the method won’t change).