For Development HEAD DRAFTSearch (procedure/syntax/module):

7.4 Generic function and method

Defining methods

Macro: define-generic name :key class

Creates a generic function and bind it to name.

You don’t usually need to use this, since the define-method macro implicitly creates a generic function if it doesn’t exist yet.

You can pass a subclass of <generic> to the class keyword argument so that the created generic function will be the instance of the passed class, instead of the default <generic> class. It is useful when you defined a subclass of <generic> to customize generic function application behavior.

Macro: define-method name [qualifier …] specs body

Defines a method whose name is name. If there’s already a generic function object globally bound to name, the created method is added to the generic function. If name is unbound, or bound to an object except a generic function, then a new generic function is created, bound to name, then a new method is added to it.

The name can be followed by optional qualifiers, each of which is a keyword. Currently, only the following qualifier is valid.

:locked

Declares that you won’t redefine the method with the same specifiers. Attempt to redefine it will raise an error. (You can still define methods with different specifiers.)

Most methods concerning basic operations on built-in objects are locked, for redefining them would case Gauche’s infrastracture unstable. It also allows Gauche to perform certain optimizations.

Specs specifies the arguments and their types for this method. It’s like the argument list of lambda form, except you can specify the type of each argument.

specs : ( arg ... )
      | ( arg ... . symbol )
      | ( arg ... extended-spec ...)
      | symbol

arg   : ( symbol class )
      | symbol

Class specifies the class that the argument has to belong to. If arg is just a symbol, it is equivalent to (arg <top>). You can’t specify the type for the “rest” argument, for it is always bound to a list.

You can use extended argument specifications such as :optional, :key and :rest as well. (See Making procedures, for the explanation of extended argument specifications). Those extended arguments are treated as if a single “rest” argument in terms of dispatching; they aren’t used for method dispatch, and you can’t specify classes for these optional and keyword arguments.

The list of classes of the argument list is called method specializer list, based on which the generic function will select appropriate methods(s). Here are some examples of specs and the corresponding specializer list (note that the rest argument isn’t considered as a part of specializer list; we know it’s always a list.) The optional item indicates whether the method takes rest arguments or not.

specs:        ((self <myclass>) (index <integer>) value)
specializers: (<myclass> <integer> <top>)
optional:     #f

specs:        (obj (attr <string>))
specializers: (<top> <string>)
optional:     #f

specs:        ((self <myclass>) obj . options)
specializers: (<myclass> <top>)
optional:     #t

specs:        ((self <myclass>) obj :optional (a 0) (b 1) :key (c 2))
specializers: (<myclass> <top>)
optional:     #t

specs:        args
specializers: ()
optional:     #t

If you define a method on name whose specializer list, and whether it takes rest arguments, match with one in the generic function’s methods, then the existing method is replaced by the newly defined one, unless the original method is locked.

Note: If you’re running Gauche with keyword-symbol integrated mode (see Keyword and symbol integration), there’s an ambiguity if you specify a keyword as the sole specs (to receive entire arguments in a single variable). Gauche parses keywords following name as qualifiers, so avoid using a keyword as such a variable.

Applying generic function

When a generic function is applied, first it selects methods whose specializer list matches the given arguments. For example, suppose a generic function foo has three methods, whose specializer lists are (<string> <top>), (<string> <string>), and (<top> <top>), respectively. When foo is applied like (foo "abc" 3), the first and the third method will be selected.

Then the selected methods are sorted from the most specific method to the least specific method. It is calculated as follows:

Once methods are sorted, the body of the first method is called with the actual argument.

Within the method body, a special local variable next-method is bound implicitly.

Next method: next-method
Next method: next-method args …

This variable is bound within a method body to a special object that encapsulates the next method in the sorted method list.

Calling without arguments invokes the next method with the same arguments as this method is called with. Passing args … explicitly invokes the next method with the passed arguments.

If next-method is called in the least specific method, i.e. there’s no "next method", an error is signaled.



For Development HEAD DRAFTSearch (procedure/syntax/module):
DRAFT