(rontolisp) docs
← Special Forms

defmethod

(defmethod name [qualifier] (param... ) body...)

Adds a method to the generic function name (creating it when no defgeneric preceded it) and returns the name symbol. Any required parameter may carry a specializer, written (var specializer):

  • (var (eql literal)) — matches when the argument is the literal (a keyword, quoted symbol, number, or character). A bare name is looked up as a defconstant defined earlier and matches its value; a name that is no such constant stands for the symbol itself
  • (var class-name) — matches instances of a defclass class and its subclasses
  • (var struct-name) — matches instances of a defstruct type (the dispatcher tests the instance tag, like the struct predicate)
  • (var type-name) — matches a built-in type (integer, float, number, string, symbol, keyword, character, cons, list, null, hash-table, function, pathname, package, ...). A package parameter matches exactly what typep calls a package, and is tried BEFORE keyword/symbol, so the designator idiom "a package method plus an unspecialized method that calls find-package and recurses" terminates
  • (var t) or a plain var — the default method

A call runs the most specific matching method: parameters are ranked leftmost-first, and per parameter eql methods win over class methods (subclass before superclass), then built-in types (subtypes such as integer before their supertypes such as number), then the default; with no match the call signals an error. Defining the same specializer combination again replaces the previous method. The lambda list may continue past the required parameters with &optional/&rest/&key (the dispatcher forwards the tail via apply). The body may start with a docstring and (declare ...) (both are ignored).

Lambda-list congruence

Every method's lambda list must be congruent with the generic function's (CLHS 7.6.4): the same number of required parameters and of &optional parameters, &rest or &key in both or in neither, and every keyword a defgeneric names after &key accepted by the method (named in its &key, or through &allow-other-keys, or by &rest without &key). The generic's lambda list is its defgeneric's, else the first method's; initialize-instance, reinitialize-instance, shared-initialize and print-object have their standard ones. A method that is not congruent is not added: the defmethod signals a program-error where it runs, on every backend.

(defgeneric scale (x factor &optional offset))
(defmethod scale ((x integer) factor) (* x factor))
;; Unhandled condition: DEFMETHOD SCALE: the method has fewer optional arguments than the generic function

Setf methods

name may also be the function name (setf reader): the method becomes part of the setf function of reader, and (setf (reader arg...) value) dispatches through it with the new value as the FIRST parameter (CL's setf-function argument order). A setf method on the same name as a defclass :accessor merges with the accessor's writer methods instead of shadowing them, and #'(setf reader) is the writer as a first-class function. (defgeneric (setf reader) ...) works the same way, inline (:method ...) clauses included.

Method qualifiers and call-next-method

An optional :before, :after, or :around qualifier before the lambda list adds an auxiliary method (standard method combination). For one call:

  • every applicable :around method runs, most specific first, each wrapping the rest;
  • then every :before method runs for effect, most specific first;
  • then the most specific applicable primary (unqualified) method runs — its value is the result;
  • then every :after method runs for effect, least specific first.

Under a short-form :method-combination the qualifier set is different: a primary method carries the COMBINATION NAME instead ((defmethod total + ((x account)) ...)), :around still wraps, and :before/:after are rejected.

Inside a primary or :around method, (call-next-method) invokes the next less specific method (passing the current arguments, or new ones if given as (call-next-method arg...)), and (next-method-p) returns whether such a method exists. Calling call-next-method with no next method signals an error.

Lite subset: standard method combination is supported for class and default methods (an :around/:before/:after with an eql or built-in-type specializer combines only with primaries of the same specializer plus the default method). On the compilation path defmethod is only supported as a top-level form; the dispatched method set of a compiled program is fixed at compile time.

A method on a built-in name

Defining a method on the name of a built-in function (close, open-stream-p, stream-element-type, ...) makes the built-in the generic function's default method: instances of the specialized class run the method, and every other argument keeps the built-in behavior — including through a (call-next-method) out of the least specific primary method. A method on close for your own stream class therefore leaves (close stream) on a real file stream working. A user default (unspecialized) method still replaces the built-in outright.

Lite subset: this works on every backend — the compilation paths route calls to such a name through the generated dispatcher, whose fall-through is the original built-in. Only names backed by a native built-in function participate; a name implemented as an expansion (mapcar, sort, format, ...) cannot take methods, and a plain defun on a built-in name is still ignored on the compilation paths.