| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
[ *fixme* Somewhere in this manual there needs to be an introductory discussion about GOOPS classes, generic functions and methods, covering
Some of this is covered in the Tutorial chapter, in 4.5.1 Generic functions and methods - perhaps the best solution would be to expand the discussion there. ]
2.2.1 Basic Class Definition 2.2.2 Class Options 2.2.3 Slot Options 2.2.4 Class Definition Internals 2.2.5 Customizing Class Definition 2.2.6 STKlos Compatibility
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
New classes are defined using the define-class syntax, with
arguments that specify the classes that the new class should inherit
from, the direct slots of the new class, and any required class options.
Each slot-definition is either a symbol that names the slot or a list,
(slot-name-symbol . slot-options) |
where slot-name-symbol is a symbol and slot-options is a list with an even number of elements. The even-numbered elements of slot-options (counting from zero) are slot option keywords; the odd-numbered elements are the corresponding values for those keywords.
options is a similarly structured list containing class option keywords and corresponding values.
The standard GOOPS class and slot options are described in the following subsections: see 2.2.2 Class Options and 2.2.3 Slot Options.
Example 1. Define a class that combines two pre-existing classes by inheritance but adds no new slots.
(define-class <combined> (<tree> <bicycle>)) |
Example 2. Define a regular-polygon class with slots for side
length and number of sides that have default values and can be accessed
via the generic functions side-length and num-sides.
(define-class <regular-polygon> () (sl #:init-value 1 #:accessor side-length) (ns #:init-value 5 #:accessor num-sides)) |
Example 3. Define a class whose behavior (and that of its instances) is customized via an application-defined metaclass.
(define-class <tcpip-fsm> () (s #:init-value #f #:accessor state) ... #:metaclass <finite-state-class>) |
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
#:metaclass class option specifies the metaclass of the class
being defined. metaclass must be a class that inherits from
<class>. For an introduction to the use of metaclasses, see
2.1.1 Metaobjects and the Metaobject Protocol and 2.1.2.1 Metaclass.
If the #:metaclass option is absent, GOOPS reuses or constructs a
metaclass for the new class by calling ensure-metaclass
(see section ensure-metaclass).
#:name class option specifies the new class's name. This
name is used to identify the class whenever related objects - the class
itself, its instances and its subclasses - are printed.
If the #:name option is absent, GOOPS uses the first argument to
define-class as the class name.
#:environment option specifies the environment in which the
class's getters and setters are computed and evaluated.
If the #:environment option is not specified, the class's
environment defaults to the top-level environment in which the
define-class form appears.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
#:allocation option tells GOOPS how to allocate storage for
the slot. Possible values for allocation are
#:instance
Indicates that GOOPS should create separate storage for this slot in each new instance of the containing class (and its subclasses).
#:class
Indicates that GOOPS should create storage for this slot that is shared
by all instances of the containing class (and its subclasses). In other
words, a slot in class C with allocation #:class is shared
by all instances for which (is-a? instance c).
#:each-subclass
Indicates that GOOPS should create storage for this slot that is shared
by all direct instances of the containing class, and that
whenever a subclass of the containing class is defined, GOOPS should
create a new storage for the slot that is shared by all direct
instances of the subclass. In other words, a slot with allocation
#:each-subclass is shared by all instances with the same
class-of.
#:virtual
Indicates that GOOPS should not allocate storage for this slot. The
slot definition must also include the #:slot-ref and
#:slot-set! options to specify how to reference and set the value
for this slot.
The default value is #:instance.
Slot allocation options are processed when defining a new class by the
generic function compute-get-n-set, which is specialized by the
class's metaclass. Hence new types of slot allocation can be
implemented by defining a new metaclass and a method for
compute-get-n-set that is specialized for the new metaclass. For
an example of how to do this, see 2.2.5 Customizing Class Definition.
#:slot-ref and #:slot-set! options must be specified
if the slot allocation is #:virtual, and are ignored otherwise.
getter should be a closure taking a single instance parameter that returns the current slot value. setter should be a closure taking two parameters - instance and new-val - that sets the slot value to new-val.
slot-ref and slot-set!.
getter specifies a generic function to which GOOPS will add a method for getting the slot value. setter specifies a generic function to which GOOPS will add a method for setting the slot value. accessor specifies an accessor to which GOOPS will add methods for both getting and setting the slot value.
So if a class includes a slot definition like this:
(c #:getter get-count #:setter set-count #:accessor count) |
GOOPS defines generic function methods such that the slot value can be referenced using either the getter or the accessor -
(let ((current-count (get-count obj))) ...) (let ((current-count (count obj))) ...) |
- and set using either the setter or the accessor -
(set-count obj (+ 1 current-count)) (set! (count obj) (+ 1 current-count)) |
Note that
set! syntax
#:getter, #:setter and #:accessor options
respectively.
If the specified names are already bound in the top-level environment to
values that cannot be upgraded to generic functions, those values are
overwritten during evaluation of the define-class that contains
the slot definition. For details, see ensure-generic.
make when creating a new instance.
If more than one of these options is specified for the same slot, the order of precedence, highest first is
#:init-keyword, if init-keyword is present in the options
passed to make
#:init-thunk, #:init-form or #:init-value.
If the slot definition contains more than one initialization option of the same precedence, the later ones are ignored. If a slot is not initialized at all, its value is unbound.
In general, slots that are shared between more than one instance are only initialized at new instance creation time if the slot value is unbound at that time. However, if the new instance creation specifies a valid init keyword and value for a shared slot, the slot is re-initialized regardless of its previous value.
Note, however, that the power of GOOPS' metaobject protocol means that
everything written here may be customized or overridden for particular
classes! The slot initializations described here are performed by the least
specialized method of the generic function initialize, whose
signature is
(define-method (initialize (object <object>) initargs) ...) |
The initialization of instances of any given class can be customized by
defining a initialize method that is specialized for that class,
and the author of the specialized method may decide to call
next-method - which will result in a call to the next less
specialized initialize method - at any point within the
specialized code, or maybe not at all. In general, therefore, the
initialization mechanisms described here may be modified or overridden by
more specialized code, or may not be supported at all for particular
classes.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Implementation notes: define-class expands to an expression which
class to create the new class (see section class)
class-redefinition
(see section 2.8 Redefining a Class).
Implementation notes: class expands to an expression which
#:init-form option to an
#:init-thunk option, to supply a default environment parameter
(the current top-level environment) and to evaluate all the bits that
need to be evaluated
make-class to create the class with the processed and
evaluated parameters.
make-class,
slots and options are separate list parameters: slots
here is a list of slot definitions.
Implementation notes: make-class
<object> to the supers list if supers is empty
or if none of the classes in supers have <object> in their
class precedence list
#:environment, #:name and #:metaclass
options, if they are not specified by options, to the current
top-level environment, the unbound value, and (ensure-metaclass
supers) respectively (see section ensure-metaclass)
make, passing the metaclass as the first parameter and all
other parameters as option keywords with values.
In the simplest case, where all the supers are straightforward
classes with metaclass <class>, the returned metaclass is just
<class>.
For a more complex example, suppose that supers contained one
class with metaclass <operator-class> and one with metaclass
<foreign-object-class>. Then the returned metaclass would be a
class that inherits from both <operator-class> and
<foreign-object-class>.
If supers is the empty list, ensure-metaclass returns the
default GOOPS metaclass <class>.
GOOPS keeps a list of the metaclasses created by
ensure-metaclass, so that each required type of metaclass only
has to be created once.
The env parameter is ignored.
ensure-metaclass-with-supers is an internal procedure used by
ensure-metaclass (see section ensure-metaclass). It returns a metaclass that is the union by
inheritance of the metaclasses in meta-supers.
The internals of make, which is ultimately used to create the new
class object, are described in 2.3.2 Customizing Instance Creation,
which covers the creation and initialization of instances in general.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
During the initialization of a new class, GOOPS calls a number of generic functions with the newly allocated class instance as the first argument. Specifically, GOOPS calls the generic function
where class is the newly allocated class instance, and the default
initialize method for arguments of type <class> calls the
generic functions
compute-slots
compute-slots that includes a
#:getter or #:accessor slot option
compute-slots that includes a
#:setter or #:accessor slot option.
If the metaclass of the new class is something more specialized than the
default <class>, then the type of class in the calls above
is more specialized than <class>, and hence it becomes possible
to define generic function methods, specialized for the new class's
metaclass, that can modify or override the default behaviour of
initialize, compute-cpl or compute-get-n-set.
compute-cpl computes the class precedence list ("CPL") for the
new class (see section 4.4.4 Class precedence list), and returns it as a list of
class objects. The CPL is important because it defines a superclass
ordering that is used, when a generic function is invoked upon an
instance of the class, to decide which of the available generic function
methods is the most specific. Hence compute-cpl could be
customized in order to modify the CPL ordering algorithm for all classes
with a special metaclass.
The default CPL algorithm is encapsulated by the compute-std-cpl
procedure, which is in turn called by the default compute-cpl
method.
compute-slots computes and returns a list of all slot definitions
for the new class. By default, this list includes the direct slot
definitions from the define-class form, plus the slot definitions
that are inherited from the new class's superclasses. The default
compute-slots method uses the CPL computed by compute-cpl
to calculate this union of slot definitions, with the rule that slots
inherited from superclasses are shadowed by direct slots with the same
name. One possible reason for customizing compute-slots would be
to implement an alternative resolution strategy for slot name conflicts.
compute-get-n-set computes the low-level closures that will be
used to get and set the value of a particular slot, and returns them in
a list with two elements.
The closures returned depend on how storage for that slot is allocated.
The standard compute-get-n-set method, specialized for classes of
type <class>, handles the standard GOOPS values for the
#:allocation slot option (see section allocation). By
defining a new compute-get-n-set method for a more specialized
metaclass, it is possible to support new types of slot allocation.
Suppose you wanted to create a large number of instances of some class with a slot that should be shared between some but not all instances of that class - say every 10 instances should share the same slot storage. The following example shows how to implement and use a new type of slot allocation to do this.
(define-class <batched-allocation-metaclass> (<class>))
(let ((batch-allocation-count 0)
(batch-get-n-set #f))
(define-method (compute-get-n-set (class <batched-allocation-metaclass>) s)
(case (slot-definition-allocation s)
((#:batched)
;; If we've already used the same slot storage for 10 instances,
;; reset variables.
(if (= batch-allocation-count 10)
(begin
(set! batch-allocation-count 0)
(set! batch-get-n-set #f)))
;; If we don't have a current pair of get and set closures,
;; create one. make-closure-variable returns a pair of closures
;; around a single Scheme variable - see goops.scm for details.
(or batch-get-n-set
(set! batch-get-n-set (make-closure-variable)))
;; Increment the batch allocation count.
(set! batch-allocation-count (+ batch-allocation-count 1))
batch-get-n-set)
;; Call next-method to handle standard allocation types.
(else (next-method)))))
(define-class <class-using-batched-slot> ()
...
(c #:allocation #:batched)
...
#:metaclass <batched-allocation-metaclass>)
|
The usage of compute-getter-method and compute-setter-method
is described in 3. MOP Specification.
compute-cpl and compute-get-n-set are called by the
standard initialize method for classes whose metaclass is
<class>. But initialize itself can also be modified, by
defining an initialize method specialized to the new class's
metaclass. Such a method could complete override the standard
behaviour, by not calling (next-method) at all, but more
typically it would perform additional class initialization steps before
and/or after calling (next-method) for the standard behaviour.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
If the STKlos compatibility module is loaded, define-class is
overwritten by a STKlos-specific definition; the standard GOOPS
definition of define-class remains available in
standard-define-class.
standard-define-class is equivalent to the standard GOOPS
define-class.
| [ << ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |