1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-04-30 20:00:19 +02:00
guile/doc/ref/goops.texi
Neil Jerram 4431a337f0 Put `figures' into standard texinfo form
* doc/ref/goops.texi (Example, Inheritance): Remove unnecessary figure
  references.  Use @float and @ref.
  (Class precedence list): Ditto.
2010-12-06 23:02:12 +00:00

3140 lines
111 KiB
Text

@c -*-texinfo-*-
@c This is part of the GNU Guile Reference Manual.
@c Copyright (C) 2008, 2009
@c Free Software Foundation, Inc.
@c See the file guile.texi for copying conditions.
@macro goops
GOOPS
@end macro
@macro guile
Guile
@end macro
@node GOOPS
@chapter GOOPS
@goops{} is the object oriented extension to @guile{}. Its
implementation is derived from @w{STk-3.99.3} by Erick Gallesio and
version 1.3 of Gregor Kiczales' @cite{Tiny-Clos}. It is very close in
spirit to CLOS, the Common Lisp Object System, but is adapted for the
Scheme language.
@goops{} is a full object oriented system, with classes, objects,
multiple inheritance, and generic functions with multi-method
dispatch. Furthermore its implementation relies on a meta object
protocol --- which means that @goops{}'s core operations are themselves
defined as methods on relevant classes, and can be customised by
overriding or redefining those methods.
To start using @goops{} you first need to import the @code{(oop goops)}
module. You can do this at the Guile REPL by evaluating:
@lisp
(use-modules (oop goops))
@end lisp
@findex (oop goops)
@menu
* Copyright Notice::
* Class Definition::
* Instance Creation::
* Slot Options::
* Slot Description Example::
* Methods and Generic Functions::
* Inheritance::
* Class Options::
* Accessing Slots::
* Generic Functions and Accessors::
* Adding Methods to Generic Functions::
* Invoking Generic Functions::
* Redefining a Class::
* Changing the Class of an Instance::
* Introspection::
* GOOPS Error Handling::
* Object Comparisons::
* Cloning Objects::
* Write and Display::
* The Metaobject Protocol::
@end menu
@node Copyright Notice
@section Copyright Notice
The material in this chapter is partly derived from the STk Reference
Manual written by Erick Gallesio, whose copyright notice is as follows.
Copyright © 1993-1999 Erick Gallesio - I3S-CNRS/ESSI <eg@@unice.fr>
Permission to use, copy, modify, distribute,and license this
software and its documentation for any purpose is hereby granted,
provided that existing copyright notices are retained in all
copies and that this notice is included verbatim in any
distributions. No written agreement, license, or royalty fee is
required for any of the authorized uses.
This software is provided ``AS IS'' without express or implied
warranty.
The material has been adapted for use in Guile, with the author's
permission.
@node Class Definition
@section Class Definition
A new class is defined with the @code{define-class} syntax:
@findex define-class
@cindex class
@lisp
(define-class @var{class} (@var{superclass} @dots{})
@var{slot-description} @dots{}
@var{class-option} @dots{})
@end lisp
@var{class} is the class being defined. The list of @var{superclass}es
specifies which existing classes, if any, to inherit slots and
properties from. @dfn{Slots} hold per-instance@footnote{Usually --- but
see also the @code{#:allocation} slot option.} data, for instances of
that class --- like ``fields'' or ``member variables'' in other object
oriented systems. Each @var{slot-description} gives the name of a slot
and optionally some ``properties'' of this slot; for example its initial
value, the name of a function which will access its value, and so on.
Slot descriptions and inheritance are discussed more below. For class
options, see @ref{Class Options}.
@cindex slot
@deffn syntax define-class name (super @dots{}) slot-definition @dots{} . options
Define a class called @var{name} that inherits from @var{super}s, with
direct slots defined by @var{slot-definition}s and class options
@var{options}. The newly created class is bound to the variable name
@var{name} in the current environment.
Each @var{slot-definition} is either a symbol that names the slot or a
list,
@example
(@var{slot-name-symbol} . @var{slot-options})
@end example
where @var{slot-name-symbol} is a symbol and @var{slot-options} is a
list with an even number of elements. The even-numbered elements of
@var{slot-options} (counting from zero) are slot option keywords; the
odd-numbered elements are the corresponding values for those keywords.
@var{options} is a similarly structured list containing class option
keywords and corresponding values.
@end deffn
As an example, let us define a type for representing a complex number
in terms of two real numbers.@footnote{Of course Guile already
provides complex numbers, and @code{<complex>} is in fact a predefined
class in GOOPS; but the definition here is still useful as an
example.} This can be done with the following class definition:
@lisp
(define-class <my-complex> (<number>)
r i)
@end lisp
This binds the variable @code{<my-complex>} to a new class whose
instances will contain two slots. These slots are called @code{r} and
@code{i} and will hold the real and imaginary parts of a complex
number. Note that this class inherits from @code{<number>}, which is a
predefined class.@footnote{@code{<number>} is the direct superclass of
the predefined class @code{<complex>}; @code{<complex>} is the
superclass of @code{<real>}, and @code{<real>} is the superclass of
@code{<integer>}.}
The possible slot and class options are described in the following
sections.
@node Instance Creation
@section Instance Creation and Slot Access
An instance (or object) of a defined class can be created with
@code{make}. @code{make} takes one mandatory parameter, which is the
class of the instance to create, and a list of optional arguments that
will be used to initialize the slots of the new instance. For instance
the following form
@findex make
@cindex instance
@lisp
(define c (make <my-complex>))
@end lisp
@noindent
creates a new @code{<my-complex>} object and binds it to the Scheme
variable @code{c}.
@deffn generic make
@deffnx method make (class <class>) . initargs
Create and return a new instance of class @var{class}, initialized using
@var{initargs}.
In theory, @var{initargs} can have any structure that is understood by
whatever methods get applied when the @code{initialize} generic function
is applied to the newly allocated instance.
In practice, specialized @code{initialize} methods would normally call
@code{(next-method)}, and so eventually the standard GOOPS
@code{initialize} methods are applied. These methods expect
@var{initargs} to be a list with an even number of elements, where
even-numbered elements (counting from zero) are keywords and
odd-numbered elements are the corresponding values.
GOOPS processes initialization argument keywords automatically for slots
whose definition includes the @code{#:init-keyword} option (@pxref{Slot
Options,, init-keyword}). Other keyword value pairs can only be
processed by an @code{initialize} method that is specialized for the new
instance's class. Any unprocessed keyword value pairs are ignored.
@end deffn
@deffn generic make-instance
@deffnx method make-instance (class <class>) . initargs
@code{make-instance} is an alias for @code{make}.
@end deffn
The slots of the new complex number can be accessed using
@code{slot-ref} and @code{slot-set!}. @code{slot-set!} sets the value
of an object slot and @code{slot-ref} retrieves it.
@findex slot-set!
@findex slot-ref
@lisp
@group
(slot-set! c 'r 10)
(slot-set! c 'i 3)
(slot-ref c 'r) @result{} 10
(slot-ref c 'i) @result{} 3
@end group
@end lisp
The @code{(oop goops describe)} module provides a @code{describe}
function that is useful for seeing all the slots of an object; it prints
the slots and their values to standard output.
@lisp
(describe c)
@print{}
#<<my-complex> 401d8638> is an instance of class <my-complex>
Slots are:
r = 10
i = 3
@end lisp
@node Slot Options
@section Slot Options
When specifying a slot (in a @code{(define-class @dots{})} form),
various options can be specified in addition to the slot's name. Each
option is specified by a keyword. The list of possible keywords is
as follows.
@deffn {slot option} #:init-value init-value
@deffnx {slot option} #:init-form init-form
@deffnx {slot option} #:init-thunk init-thunk
@deffnx {slot option} #:init-keyword init-keyword
These options provide various ways to specify how to initialize the
slot's value at instance creation time.
@cindex default slot value
@var{init-value} specifies a fixed initial slot value (shared across all
new instances of the class).
@var{init-thunk} specifies a thunk that will provide a default value for
the slot. The thunk is called when a new instance is created and should
return the desired initial slot value.
@var{init-form} specifies a form that, when evaluated, will return
an initial value for the slot. The form is evaluated each time that
an instance of the class is created, in the lexical environment of the
containing @code{define-class} expression.
@var{init-keyword} specifies a keyword that can be used to pass an
initial slot value to @code{make} when creating a new instance.
Note that, since an @code{init-value} value is shared across all
instances of a class, you should only use it when the initial value is
an immutable value, like a constant. If you want to initialize a slot
with a fresh, independently mutable value, you should use
@code{init-thunk} or @code{init-form} instead. Consider the following
example.
@example
(define-class <chbouib> ()
(hashtab #:init-value (make-hash-table)))
@end example
@noindent
Here only one hash table is created and all instances of
@code{<chbouib>} have their @code{hashtab} slot refer to it. In order
to have each instance of @code{<chbouib>} refer to a new hash table, you
should instead write:
@example
(define-class <chbouib> ()
(hashtab #:init-thunk make-hash-table))
@end example
@noindent
or:
@example
(define-class <chbouib> ()
(hashtab #:init-form (make-hash-table)))
@end example
If more than one of these options is specified for the same slot, the
order of precedence, highest first is
@itemize @bullet
@item
@code{#:init-keyword}, if @var{init-keyword} is present in the options
passed to @code{make}
@item
@code{#:init-thunk}, @code{#:init-form} or @code{#:init-value}.
@end itemize
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 @code{initialize}, whose
signature is
@example
(define-method (initialize (object <object>) initargs) ...)
@end example
The initialization of instances of any given class can be customized by
defining a @code{initialize} method that is specialized for that class,
and the author of the specialized method may decide to call
@code{next-method} - which will result in a call to the next less
specialized @code{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.
@end deffn
@deffn {slot option} #:getter getter
@deffnx {slot option} #:setter setter
@deffnx {slot option} #:accessor accessor
Given an object @var{obj} with slots named @code{foo} and @code{bar}, it
is always possible to read and write those slots by calling
@code{slot-ref} and @code{slot-set!} with the relevant slot name; for
example:
@example
(slot-ref @var{obj} 'foo)
(slot-set! @var{obj} 'bar 25)
@end example
The @code{#:getter}, @code{#:setter} and @code{#:accessor} options, if
present, tell GOOPS to create generic function and method definitions
that can be used to get and set the slot value more conveniently.
@var{getter} specifies a generic function to which GOOPS will add a
method for getting the slot value. @var{setter} specifies a generic
function to which GOOPS will add a method for setting the slot value.
@var{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:
@example
(c #:getter get-count #:setter set-count #:accessor count)
@end example
GOOPS defines generic function methods such that the slot value can be
referenced using either the getter or the accessor -
@example
(let ((current-count (get-count obj))) @dots{})
(let ((current-count (count obj))) @dots{})
@end example
- and set using either the setter or the accessor -
@example
(set-count obj (+ 1 current-count))
(set! (count obj) (+ 1 current-count))
@end example
Note that
@itemize @bullet
@item
with an accessor, the slot value is set using the generalized
@code{set!} syntax
@item
in practice, it is unusual for a slot to use all three of these options:
read-only, write-only and read-write slots would typically use only
@code{#:getter}, @code{#:setter} and @code{#:accessor} options
respectively.
@end itemize
The binding of the specified names is done in the environment of the
@code{define-class} expression. If the names are already bound (in that
environment) to values that cannot be upgraded to generic functions,
those values are overwritten when the @code{define-class} expression is
evaluated. For more detail, see @ref{Generic Function Internals,,
ensure-generic}.
@end deffn
@deffn {slot option} #:allocation allocation
The @code{#:allocation} option tells GOOPS how to allocate storage for
the slot. Possible values for @var{allocation} are
@itemize @bullet
@item @code{#:instance}
@findex #:instance
Indicates that GOOPS should create separate storage for this slot in
each new instance of the containing class (and its subclasses). This is
the default.
@item @code{#:class}
@findex #: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 @var{C} with allocation @code{#:class} is shared
by all @var{instance}s for which @code{(is-a? @var{instance} @var{c})}.
This permits defining a kind of global variable which can be accessed
only by (in)direct instances of the class which defines the slot.
@item @code{#:each-subclass}
@findex #:each-subclass
Indicates that GOOPS should create storage for this slot that is shared
by all @emph{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 @emph{direct}
instances of the subclass. In other words, a slot with allocation
@code{#:each-subclass} is shared by all instances with the same
@code{class-of}.
@item @code{#:virtual}
@findex #:slot-set!
@findex #:slot-ref
@findex #:virtual
Indicates that GOOPS should not allocate storage for this slot. The
slot definition must also include the @code{#:slot-ref} and
@code{#:slot-set!} options to specify how to reference and set the value
for this slot. See the example below.
@end itemize
Slot allocation options are processed when defining a new class by the
generic function @code{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
@code{compute-get-n-set} that is specialized for the new metaclass. For
an example of how to do this, see @ref{Customizing Class Definition}.
@end deffn
@deffn {slot option} #:slot-ref getter
@deffnx {slot option} #:slot-set! setter
The @code{#:slot-ref} and @code{#:slot-set!} options must be specified
if the slot allocation is @code{#:virtual}, and are ignored otherwise.
@var{getter} should be a closure taking a single @var{instance} parameter
that returns the current slot value. @var{setter} should be a closure
taking two parameters - @var{instance} and @var{new-val} - that sets the
slot value to @var{new-val}.
@end deffn
@node Slot Description Example
@section Illustrating Slot Description
To illustrate slot description, we can redefine the @code{<my-complex>}
class seen before. A definition could be:
@lisp
(define-class <my-complex> (<number>)
(r #:init-value 0 #:getter get-r #:setter set-r! #:init-keyword #:r)
(i #:init-value 0 #:getter get-i #:setter set-i! #:init-keyword #:i))
@end lisp
@noindent
With this definition, the @code{r} and @code{i} slots are set to 0 by
default, and can be initialised to other values by calling @code{make}
with the @code{#:r} and @code{#:i} keywords. Also the generic functions
@code{get-r}, @code{set-r!}, @code{get-i} and @code{set-i!} are
automatically defined to read and write the slots.
@lisp
(define c1 (make <my-complex> #:r 1 #:i 2))
(get-r c1) @result{} 1
(set-r! c1 12)
(get-r c1) @result{} 12
(define c2 (make <my-complex> #:r 2))
(get-r c2) @result{} 2
(get-i c2) @result{} 0
@end lisp
Accessors can both read and write a slot. So, another definition of the
@code{<my-complex>} class, using the @code{#:accessor} option, could be:
@findex set!
@lisp
(define-class <my-complex> (<number>)
(r #:init-value 0 #:accessor real-part #:init-keyword #:r)
(i #:init-value 0 #:accessor imag-part #:init-keyword #:i))
@end lisp
@noindent
With this definition, the @code{r} slot can be read with:
@lisp
(real-part c)
@end lisp
@noindent
and set with:
@lisp
(set! (real-part c) new-value)
@end lisp
Suppose now that we want to manipulate complex numbers with both
rectangular and polar coordinates. One solution could be to have a
definition of complex numbers which uses one particular representation
and some conversion functions to pass from one representation to the
other. A better solution is to use virtual slots, like this:
@lisp
(define-class <my-complex> (<number>)
;; True slots use rectangular coordinates
(r #:init-value 0 #:accessor real-part #:init-keyword #:r)
(i #:init-value 0 #:accessor imag-part #:init-keyword #:i)
;; Virtual slots access do the conversion
(m #:accessor magnitude #:init-keyword #:magn
#:allocation #:virtual
#:slot-ref (lambda (o)
(let ((r (slot-ref o 'r)) (i (slot-ref o 'i)))
(sqrt (+ (* r r) (* i i)))))
#:slot-set! (lambda (o m)
(let ((a (slot-ref o 'a)))
(slot-set! o 'r (* m (cos a)))
(slot-set! o 'i (* m (sin a))))))
(a #:accessor angle #:init-keyword #:angle
#:allocation #:virtual
#:slot-ref (lambda (o)
(atan (slot-ref o 'i) (slot-ref o 'r)))
#:slot-set! (lambda(o a)
(let ((m (slot-ref o 'm)))
(slot-set! o 'r (* m (cos a)))
(slot-set! o 'i (* m (sin a)))))))
@end lisp
In this class definition, the magniture @code{m} and angle @code{a}
slots are virtual, and are calculated, when referenced, from the normal
(i.e. @code{#:allocation #:instance}) slots @code{r} and @code{i}, by
calling the function defined in the relevant @code{#:slot-ref} option.
Correspondingly, writing @code{m} or @code{a} leads to calling the
function defined in the @code{#:slot-set!} option. Thus the
following expression
@findex #:slot-set!
@findex #:slot-ref
@lisp
(slot-set! c 'a 3)
@end lisp
@noindent
permits to set the angle of the @code{c} complex number.
@lisp
(define c (make <my-complex> #:r 12 #:i 20))
(real-part c) @result{} 12
(angle c) @result{} 1.03037682652431
(slot-set! c 'i 10)
(set! (real-part c) 1)
(describe c)
@print{}
#<<my-complex> 401e9b58> is an instance of class <my-complex>
Slots are:
r = 1
i = 10
m = 10.0498756211209
a = 1.47112767430373
@end lisp
Since initialization keywords have been defined for the four slots, we
can now define the standard Scheme primitives @code{make-rectangular}
and @code{make-polar}.
@lisp
(define make-rectangular
(lambda (x y) (make <my-complex> #:r x #:i y)))
(define make-polar
(lambda (x y) (make <my-complex> #:magn x #:angle y)))
@end lisp
@node Methods and Generic Functions
@section Methods and Generic Functions
A GOOPS method is like a Scheme procedure except that it is specialized
for a particular set of argument classes, and will only be used when the
actual arguments in a call match the classes in the method definition.
@lisp
(define-method (+ (x <string>) (y <string>))
(string-append x y))
(+ "abc" "de") @result{} "abcde"
@end lisp
A method is not formally associated with any single class (as it is in
many other object oriented languages), because a method can be
specialized for a combination of several classes. If you've studied
object orientation in non-Lispy languages, you may remember discussions
such as whether a method to stretch a graphical image around a surface
should be a method of the image class, with a surface as a parameter, or
a method of the surface class, with an image as a parameter. In GOOPS
you'd just write
@lisp
(define-method (stretch (im <image>) (sf <surface>))
...)
@end lisp
@noindent
and the question of which class the method is more associated with does
not need answering.
A generic function is a collection of methods with the same name but
different sets of specializing argument classes.
@menu
* Generic functions and methods::
* Next-method::
* Example::
@end menu
@node Generic functions and methods
@subsection Generic functions and methods
@c \label{gf-n-methods}
Neither @goops{} nor CLOS use the message mechanism for methods as most
Object Oriented language do. Instead, they use the notion of
@dfn{generic functions}. A generic function can be seen as a methods
``tanker''. When the evaluator requested the application of a generic
function, all the methods of this generic function will be grabbed and
the most specific among them will be applied. We say that a method
@var{M} is @emph{more specific} than a method @var{M'} if the class of
its parameters are more specific than the @var{M'} ones. To be more
precise, when a generic function must be ``called'' the system will:
@cindex generic function
@enumerate
@item
search among all the generic function those which are applicable
@item
sort the list of applicable methods in the ``most specific'' order
@item
call the most specific method of this list (i.e. the first method of
the sorted methods list).
@end enumerate
The definition of a generic function is done with the
@code{define-generic} macro. Definition of a new method is done with the
@code{define-method} macro. Note that @code{define-method} automatically
defines the generic function if it has not been defined
before. Consequently, most of the time, the @code{define-generic} needs
not be used.
@findex define-generic
@findex define-method
Consider the following definitions:
@lisp
(define-generic G)
(define-method (G (a <integer>) b) 'integer)
(define-method (G (a <real>) b) 'real)
(define-method (G a b) 'top)
@end lisp
The @code{define-generic} call defines @var{G} as a generic
function. Note that the signature of the generic function is not given
upon definition, contrarily to CLOS. This will permit methods with
different signatures for a given generic function, as we shall see
later. The three next lines define methods for the @var{G} generic
function. Each method uses a sequence of @dfn{parameter specializers}
that specify when the given method is applicable. A specializer permits
to indicate the class a parameter must belong to (directly or
indirectly) to be applicable. If no specializer is given, the system
defaults it to @code{<top>}. Thus, the first method definition is
equivalent to
@cindex parameter specializers
@lisp
(define-method (G (a <integer>) (b <top>)) 'integer)
@end lisp
Now, let us look at some possible calls to generic function @var{G}:
@lisp
(G 2 3) @result{} integer
(G 2 #t) @result{} integer
(G 1.2 'a) @result{} real
@c (G #3 'a) @result{} real @c was {\sharpsign}
(G #t #f) @result{} top
(G 1 2 3) @result{} error (since no method exists for 3 parameters)
@end lisp
The preceding methods use only one specializer per parameter list. Of
course, each parameter can use a specializer. In this case, the
parameter list is scanned from left to right to determine the
applicability of a method. Suppose we declare now
@lisp
(define-method (G (a <integer>) (b <number>)) 'integer-number)
(define-method (G (a <integer>) (b <real>)) 'integer-real)
(define-method (G (a <integer>) (b <integer>)) 'integer-integer)
(define-method (G a (b <number>)) 'top-number)
@end lisp
In this case,
@lisp
(G 1 2) @result{} integer-integer
(G 1 1.0) @result{} integer-real
(G 1 #t) @result{} integer
(G 'a 1) @result{} top-number
@end lisp
@node Next-method
@subsection Next-method
When you call a generic function, with a particular set of arguments,
GOOPS builds a list of all the methods that are applicable to those
arguments and orders them by how closely the method definitions match
the actual argument types. It then calls the method at the top of this
list. If the selected method's code wants to call on to the next method
in this list, it can do so by using @code{next-method}.
@lisp
(define-method (Test (a <integer>)) (cons 'integer (next-method)))
(define-method (Test (a <number>)) (cons 'number (next-method)))
(define-method (Test a) (list 'top))
@end lisp
With these definitions,
@lisp
(Test 1) @result{} (integer number top)
(Test 1.0) @result{} (number top)
(Test #t) @result{} (top)
@end lisp
@code{next-method} is always called as just @code{(next-method)}. The
arguments for the next method call are always implicit, and always the
same as for the original method call.
If you want to call on to a method with the same name but with a
different set of arguments (as you might with overloaded methods in C++,
for example), you do not use @code{next-method}, but instead simply
write the new call as usual:
@lisp
(define-method (Test (a <number>) min max)
(if (and (>= a min) (<= a max))
(display "Number is in range\n"))
(Test a))
(Test 2 1 10)
@print{}
Number is in range
@result{}
(integer number top)
@end lisp
(You should be careful in this case that the @code{Test} calls do not
lead to an infinite recursion, but this consideration is just the same
as in Scheme code in general.)
@node Example
@subsection Example
In this section we shall continue to define operations on the
@code{<my-complex>} class. Suppose that we want to use it to implement
complex numbers completely. For instance a definition for the addition
of two complexes could be
@lisp
(define-method (new-+ (a <my-complex>) (b <my-complex>))
(make-rectangular (+ (real-part a) (real-part b))
(+ (imag-part a) (imag-part b))))
@end lisp
To be sure that the @code{+} used in the method @code{new-+} is the
standard addition we can do:
@lisp
(define-generic new-+)
(let ((+ +))
(define-method (new-+ (a <my-complex>) (b <my-complex>))
(make-rectangular (+ (real-part a) (real-part b))
(+ (imag-part a) (imag-part b)))))
@end lisp
The @code{define-generic} ensures here that @code{new-+} will be defined
in the global environment. Once this is done, we can add methods to the
generic function @code{new-+} which make a closure on the @code{+}
symbol. A complete writing of the @code{new-+} methods is shown in
@ref{fig:newplus}.
@float Figure,fig:newplus
@lisp
(define-generic new-+)
(let ((+ +))
(define-method (new-+ (a <real>) (b <real>)) (+ a b))
(define-method (new-+ (a <real>) (b <my-complex>))
(make-rectangular (+ a (real-part b)) (imag-part b)))
(define-method (new-+ (a <my-complex>) (b <real>))
(make-rectangular (+ (real-part a) b) (imag-part a)))
(define-method (new-+ (a <my-complex>) (b <my-complex>))
(make-rectangular (+ (real-part a) (real-part b))
(+ (imag-part a) (imag-part b))))
(define-method (new-+ (a <number>)) a)
(define-method (new-+) 0)
(define-method (new-+ . args)
(new-+ (car args)
(apply new-+ (cdr args)))))
(set! + new-+)
@end lisp
@caption{Extending @code{+} to handle complex numbers}
@end float
@sp 3
We use here the fact that generic function are not obliged to have the
same number of parameters, contrarily to CLOS. The four first methods
implement the dyadic addition. The fifth method says that the addition
of a single element is this element itself. The sixth method says that
using the addition with no parameter always return 0. The last method
takes an arbitrary number of parameters@footnote{The parameter list for
a @code{define-method} follows the conventions used for Scheme
procedures. In particular it can use the dot notation or a symbol to
denote an arbitrary number of parameters}. This method acts as a kind
of @code{reduce}: it calls the dyadic addition on the @emph{car} of the
list and on the result of applying it on its rest. To finish, the
@code{set!} permits to redefine the @code{+} symbol to our extended
addition.
@sp 3
To terminate our implementation (integration?) of complex numbers, we can
redefine standard Scheme predicates in the following manner:
@lisp
(define-method (complex? c <my-complex>) #t)
(define-method (complex? c) #f)
(define-method (number? n <number>) #t)
(define-method (number? n) #f)
@dots{}
@dots{}
@end lisp
Standard primitives in which complex numbers are involved could also be
redefined in the same manner.
@node Inheritance
@section Inheritance
Here are some class definitions to help illustrate inheritance:
@lisp
(define-class A () a)
(define-class B () b)
(define-class C () c)
(define-class D (A B) d a)
(define-class E (A C) e c)
(define-class F (D E) f)
@end lisp
@code{A}, @code{B}, @code{C} have a null list of superclasses. In this
case, the system will replace the null list by a list which only
contains @code{<object>}, the root of all the classes defined by
@code{define-class}. @code{D}, @code{E}, @code{F} use multiple
inheritance: each class inherits from two previously defined classes.
Those class definitions define a hierarchy which is shown in
@ref{fig:hier}. In this figure, the class @code{<top>} is also shown;
this class is the superclass of all Scheme objects. In particular,
@code{<top>} is the superclass of all standard Scheme
types.@footnote{@code{<complex>}, which is the direct subclass of
@code{<number>} and the direct superclass of @code{<real>}, has been
omitted in this figure.}
@float Figure,fig:hier
@iftex
@center @image{hierarchy,5in}
@end iftex
@ifnottex
@verbatiminclude hierarchy.txt
@end ifnottex
@caption{A class hierarchy.}
@end float
When a class has superclasses, its set of slots is calculated by taking
the union of its own slots and those of all its superclasses. Thus each
instance of D will have three slots, @code{a}, @code{b} and
@code{d}). The slots of a class can be discovered using the
@code{class-slots} primitive. For instance,
@lisp
(class-slots A) @result{} ((a))
(class-slots E) @result{} ((a) (e) (c))
(class-slots F) @result{} ((e) (c) (b) (d) (a) (f))
@end lisp
@noindent
The ordering of the returned slots is not significant.
@menu
* Class precedence list::
@end menu
@node Class precedence list
@subsection Class precedence list
A class may have more than one superclass. @footnote{This section is an
adaptation of Jeff Dalton's (J.Dalton@@ed.ac.uk) @cite{Brief
introduction to CLOS}} With single inheritance (one superclass), it is
easy to order the superclasses from most to least specific. This is the
rule:
@display
@cartouche
Rule 1: Each class is more specific than its superclasses.@c was \bf
@end cartouche
@end display
With multiple inheritance, ordering is harder. Suppose we have
@lisp
(define-class X ()
(x #:init-value 1))
(define-class Y ()
(x #:init-value 2))
(define-class Z (X Y)
(@dots{}))
@end lisp
In this case, the @code{Z} class is more specific than the @code{X} or
@code{Y} class for instances of @code{Z}. However, the @code{#:init-value}
specified in @code{X} and @code{Y} leads to a problem: which one
overrides the other? The rule in @goops{}, as in CLOS, is that the
superclasses listed earlier are more specific than those listed later.
So:
@display
@cartouche
Rule 2: For a given class, superclasses listed earlier are more
specific than those listed later.
@end cartouche
@end display
These rules are used to compute a linear order for a class and all its
superclasses, from most specific to least specific. This order is
called the ``class precedence list'' of the class. Given these two
rules, we can claim that the initial form for the @code{x} slot of
previous example is 1 since the class @code{X} is placed before @code{Y}
in class precedence list of @code{Z}.
These two rules are not always enough to determine a unique order,
however, but they give an idea of how things work. Taking the @code{F}
class shown in @ref{fig:hier}, the class precedence list is
@example
(f d e a c b <object> <top>)
@end example
However, it is usually considered a bad idea for programmers to rely on
exactly what the order is. If the order for some superclasses is important,
it can be expressed directly in the class definition.
The precedence list of a class can be obtained by the function
@code{class-precedence-list}. This function returns a ordered
list whose first element is the most specific class. For instance,
@lisp
(class-precedence-list B) @result{} (#<<class> B 401b97c8>
#<<class> <object> 401e4a10>
#<<class> <top> 4026a9d8>)
@end lisp
However, this result is not too much readable; using the function
@code{class-name} yields a clearer result:
@lisp
(map class-name (class-precedence-list B)) @result{} (B <object> <top>)
@end lisp
@node Class Options
@section Class Options
@deffn {class option} #:metaclass metaclass
The @code{#:metaclass} class option specifies the metaclass of the class
being defined. @var{metaclass} must be a class that inherits from
@code{<class>}. For the use of metaclasses, see @ref{Metaobjects and
the Metaobject Protocol} and @ref{Terminology}.
If the @code{#:metaclass} option is absent, GOOPS reuses or constructs a
metaclass for the new class by calling @code{ensure-metaclass}
(@pxref{Class Definition Internals,, ensure-metaclass}).
@end deffn
@deffn {class option} #:name name
The @code{#: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 @code{#:name} option is absent, GOOPS uses the first argument to
@code{define-class} as the class name.
@end deffn
@node Accessing Slots
@section Accessing Slots
@menu
* Instance Slots::
* Class Slots::
* Handling Slot Access Errors::
@end menu
@node Instance Slots
@subsection Instance Slots
Any slot, regardless of its allocation, can be queried, referenced and
set using the following four primitive procedures.
@deffn {primitive procedure} slot-exists? obj slot-name
Return @code{#t} if @var{obj} has a slot with name @var{slot-name},
otherwise @code{#f}.
@end deffn
@deffn {primitive procedure} slot-bound? obj slot-name
Return @code{#t} if the slot named @var{slot-name} in @var{obj} has a
value, otherwise @code{#f}.
@code{slot-bound?} calls the generic function @code{slot-missing} if
@var{obj} does not have a slot called @var{slot-name} (@pxref{Handling
Slot Access Errors, slot-missing}).
@end deffn
@deffn {primitive procedure} slot-ref obj slot-name
Return the value of the slot named @var{slot-name} in @var{obj}.
@code{slot-ref} calls the generic function @code{slot-missing} if
@var{obj} does not have a slot called @var{slot-name} (@pxref{Handling
Slot Access Errors, slot-missing}).
@code{slot-ref} calls the generic function @code{slot-unbound} if the
named slot in @var{obj} does not have a value (@pxref{Handling Slot
Access Errors, slot-unbound}).
@end deffn
@deffn {primitive procedure} slot-set! obj slot-name value
Set the value of the slot named @var{slot-name} in @var{obj} to @var{value}.
@code{slot-set!} calls the generic function @code{slot-missing} if
@var{obj} does not have a slot called @var{slot-name} (@pxref{Handling
Slot Access Errors, slot-missing}).
@end deffn
GOOPS stores information about slots in classes. Internally,
all of these procedures work by looking up the slot definition for the
slot named @var{slot-name} in the class @code{(class-of
@var{obj})}, and then using the slot definition's ``getter'' and
``setter'' closures to get and set the slot value.
The next four procedures differ from the previous ones in that they take
the class as an explicit argument, rather than assuming
@code{(class-of @var{obj})}. Therefore they allow you to apply the
``getter'' and ``setter'' closures of a slot definition in one class to
an instance of a different class.
@deffn {primitive procedure} slot-exists-using-class? class obj slot-name
Return @code{#t} if @var{class} has a slot definition for a slot with
name @var{slot-name}, otherwise @code{#f}.
@end deffn
@deffn {primitive procedure} slot-bound-using-class? class obj slot-name
Return @code{#t} if applying @code{slot-ref-using-class} to the same
arguments would call the generic function @code{slot-unbound}, otherwise
@code{#f}.
@code{slot-bound-using-class?} calls the generic function
@code{slot-missing} if @var{class} does not have a slot definition for a
slot called @var{slot-name} (@pxref{Handling Slot Access Errors,
slot-missing}).
@end deffn
@deffn {primitive procedure} slot-ref-using-class class obj slot-name
Apply the ``getter'' closure for the slot named @var{slot-name} in
@var{class} to @var{obj}, and return its result.
@code{slot-ref-using-class} calls the generic function
@code{slot-missing} if @var{class} does not have a slot definition for a
slot called @var{slot-name} (@pxref{Handling Slot Access Errors,
slot-missing}).
@code{slot-ref-using-class} calls the generic function
@code{slot-unbound} if the application of the ``getter'' closure to
@var{obj} returns an unbound value (@pxref{Handling Slot Access Errors,
slot-unbound}).
@end deffn
@deffn {primitive procedure} slot-set-using-class! class obj slot-name value
Apply the ``setter'' closure for the slot named @var{slot-name} in
@var{class} to @var{obj} and @var{value}.
@code{slot-set-using-class!} calls the generic function
@code{slot-missing} if @var{class} does not have a slot definition for a
slot called @var{slot-name} (@pxref{Handling Slot Access Errors,
slot-missing}).
@end deffn
@node Class Slots
@subsection Class Slots
Slots whose allocation is per-class rather than per-instance can be
referenced and set without needing to specify any particular instance.
@deffn procedure class-slot-ref class slot-name
Return the value of the slot named @var{slot-name} in class @var{class}.
The named slot must have @code{#:class} or @code{#:each-subclass}
allocation (@pxref{Slot Options,, allocation}).
If there is no such slot with @code{#:class} or @code{#:each-subclass}
allocation, @code{class-slot-ref} calls the @code{slot-missing} generic
function with arguments @var{class} and @var{slot-name}. Otherwise, if
the slot value is unbound, @code{class-slot-ref} calls the
@code{slot-unbound} generic function, with the same arguments.
@end deffn
@deffn procedure class-slot-set! class slot-name value
Set the value of the slot named @var{slot-name} in class @var{class} to
@var{value}. The named slot must have @code{#:class} or
@code{#:each-subclass} allocation (@pxref{Slot Options,, allocation}).
If there is no such slot with @code{#:class} or @code{#:each-subclass}
allocation, @code{class-slot-ref} calls the @code{slot-missing} generic
function with arguments @var{class} and @var{slot-name}.
@end deffn
@node Handling Slot Access Errors
@subsection Handling Slot Access Errors
GOOPS calls one of the following generic functions when a ``slot-ref''
or ``slot-set!'' call specifies a non-existent slot name, or tries to
reference a slot whose value is unbound.
@deffn generic slot-missing
@deffnx method slot-missing (class <class>) slot-name
@deffnx method slot-missing (class <class>) (object <object>) slot-name
@deffnx method slot-missing (class <class>) (object <object>) slot-name value
When an application attempts to reference or set a class or instance
slot by name, and the slot name is invalid for the specified @var{class}
or @var{object}, GOOPS calls the @code{slot-missing} generic function.
The default methods all call @code{goops-error} with an appropriate
message.
@end deffn
@deffn generic slot-unbound
@deffnx method slot-unbound (object <object>)
@deffnx method slot-unbound (class <class>) slot-name
@deffnx method slot-unbound (class <class>) (object <object>) slot-name
When an application attempts to reference a class or instance slot, and
the slot's value is unbound, GOOPS calls the @code{slot-unbound} generic
function.
The default methods all call @code{goops-error} with an appropriate
message.
@end deffn
@node Generic Functions and Accessors
@section Generic Functions and Accessors
A generic function is a collection of methods, with rules for
determining which of the methods should be applied for any given
invocation of the generic function. GOOPS represents generic functions
as metaobjects of the class @code{<generic>} (or one of its subclasses).
An accessor is a generic function that can also be used with the
generalized @code{set!} syntax (@pxref{Procedures with Setters}). Guile
will handle a call like
@example
(set! (@code{accessor} @code{args}@dots{}) @code{value})
@end example
@noindent
by calling the most specialized method of @code{accessor} that matches
the classes of @code{args} and @code{value}.
The following forms define a variable as a generic function or accessor.
Depending on that variable's previous value, the generic function may be
created empty --- with no methods --- or with methods that are inferred
from the previous value.
@deffn syntax define-generic symbol
Create a generic function with name @var{symbol} and bind it to the
variable @var{symbol}. If @var{symbol} was previously bound to a Scheme
procedure (or procedure-with-setter), the old procedure (and setter) is
incorporated into the new generic function as its default procedure (and
setter). Any other previous value, including an existing generic
function, is discarded and replaced by a new, empty generic function.
@end deffn
@deffn syntax define-accessor symbol
Create an accessor with name @var{symbol} and bind it to the variable
@var{symbol}. If @var{symbol} was previously bound to a Scheme
procedure (or procedure-with-setter), the old procedure (and setter) is
incorporated into the new accessor as its default procedure (and
setter). Any other previous value, including an existing generic
function or accessor, is discarded and replaced by a new, empty
accessor.
@end deffn
@menu
* Extending Primitives::
* Merging Generics::
@end menu
@node Extending Primitives
@subsection Extending Primitives
Many of Guile's primitive procedures can be extended by giving them a
generic function definition that operates in conjunction with their
normal C-coded implementation. When a primitive is extended in this
way, it behaves like a generic function with the C-coded implementation
as its default method.
This extension happens automatically if a method is defined (by a
@code{define-method} call) for a variable whose current value is a
primitive. But it can also be forced by calling
@code{enable-primitive-generic!}.
@deffn {primitive procedure} enable-primitive-generic! primitive
Force the creation of a generic function definition for
@var{primitive}.
@end deffn
Once the generic function definition for a primitive has been created,
it can be retrieved using @code{primitive-generic-generic}.
@deffn {primitive procedure} primitive-generic-generic primitive
Return the generic function definition of @var{primitive}.
@code{primitive-generic-generic} raises an error if @var{primitive}
is not a primitive with generic capability.
@end deffn
@node Merging Generics
@subsection Merging Generics
GOOPS generic functions and accessors often have short, generic names.
For example, if a vector package provides an accessor for the X
coordinate of a vector, that accessor may just be called @code{x}. It
doesn't need to be called, for example, @code{vector:x}, because
GOOPS will work out, when it sees code like @code{(x @var{obj})}, that
the vector-specific method of @code{x} should be called if @var{obj} is
a vector.
That raises the question, however, of what happens when different
packages define a generic function with the same name. Suppose we
work with a graphical package which needs to use two independent vector
packages for 2D and 3D vectors respectively. If both packages export
@code{x}, what does the code using those packages end up with?
@ref{Creating Guile Modules,,duplicate binding handlers} explains how
this is resolved for conflicting bindings in general. For generics,
there is a special duplicates handler, @code{merge-generics}, which
tells the module system to merge generic functions with the same name.
Here is an example:
@lisp
(define-module (math 2D-vectors)
#:use-module (oop goops)
#:export (x y ...))
(define-module (math 3D-vectors)
#:use-module (oop goops)
#:export (x y z ...))
(define-module (my-module)
#:use-module (math 2D-vectors)
#:use-module (math 3D-vectors)
#:duplicates merge-generics)
@end lisp
The generic function @code{x} in @code{(my-module)} will now incorporate
all of the methods of @code{x} from both imported modules.
To be precise, there will now be three distinct generic functions named
@code{x}: @code{x} in @code{(math 2D-vectors)}, @code{x} in @code{(math
3D-vectors)}, and @code{x} in @code{(my-module)}; and these functions
share their methods in an interesting and dynamic way.
To explain, let's call the imported generic functions (in @code{(math
2D-vectors)} and @code{(math 3D-vectors)}) the @dfn{ancestors}, and the
merged generic function (in @code{(my-module)}), the @dfn{descendant}.
The general rule is that for any generic function G, the applicable
methods are selected from the union of the methods of G's descendant
functions, the methods of G itself and the methods of G's ancestor
functions.
Thus ancestor functions effectively share methods with their
descendants, and vice versa. In the example above, @code{x} in
@code{(math 2D-vectors)} will share the methods of @code{x} in
@code{(my-module)} and vice versa.@footnote{But note that @code{x} in
@code{(math 2D-vectors)} doesn't share methods with @code{x} in
@code{(math 3D-vectors)}, so modularity is still preserved.} Sharing is
dynamic, so adding another new method to a descendant implies adding it
to that descendant's ancestors too.
@node Adding Methods to Generic Functions
@section Adding Methods to Generic Functions
To add a method to a generic function, use @code{define-method}.
@deffn syntax define-method (generic parameter @dots{}) . body
Define a method for the generic function or accessor @var{generic} with
parameters @var{parameter}s and body @var{body}.
@var{generic} is a generic function. If @var{generic} is a variable
which is not yet bound to a generic function object, the expansion of
@code{define-method} will include a call to @code{define-generic}. If
@var{generic} is @code{(setter @var{generic-with-setter})}, where
@var{generic-with-setter} is a variable which is not yet bound to a
generic-with-setter object, the expansion will include a call to
@code{define-accessor}.
Each @var{parameter} must be either a symbol or a two-element list
@code{(@var{symbol} @var{class})}. The symbols refer to variables in
the @var{body} that will be bound to the parameters supplied by the
caller when calling this method. The @var{class}es, if present,
specify the possible combinations of parameters to which this method
can be applied.
@var{body} is the body of the method definition.
@end deffn
@code{define-method} expressions look a little like Scheme procedure
definitions of the form
@example
(define (name formals @dots{}) . body)
@end example
The important difference is that each formal parameter, apart from the
possible ``rest'' argument, can be qualified by a class name:
@code{@var{formal}} becomes @code{(@var{formal} @var{class})}. The
meaning of this qualification is that the method being defined
will only be applicable in a particular generic function invocation if
the corresponding argument is an instance of @code{@var{class}} (or one of
its subclasses). If more than one of the formal parameters is qualified
in this way, then the method will only be applicable if each of the
corresponding arguments is an instance of its respective qualifying class.
Note that unqualified formal parameters act as though they are qualified
by the class @code{<top>}, which GOOPS uses to mean the superclass of
all valid Scheme types, including both primitive types and GOOPS classes.
For example, if a generic function method is defined with
@var{parameter}s @code{((s1 <square>) (n <number>))}, that method is
only applicable to invocations of its generic function that have two
parameters where the first parameter is an instance of the
@code{<square>} class and the second parameter is a number.
@node Invoking Generic Functions
@section Invoking Generic Functions
When a variable with a generic function definition appears as the first
element of a list that is being evaluated, the Guile evaluator tries
to apply the generic function to the arguments obtained by evaluating
the remaining elements of the list. [ *fixme* How do I put this in a
more Schemely and less Lispy way? ]
Usually a generic function contains several method definitions, with
varying degrees of formal parameter specialization (@pxref{Adding
Methods to Generic Functions,, define-method}). So it is necessary to
sort these methods by specificity with respect to the supplied
arguments, and then apply the most specific method definition. Less
specific methods may be applied subsequently if a method that is being
applied calls @code{next-method}.
If a generic function is invoked with a combination of parameters for
which there is no applicable method, GOOPS raises an error.
@menu
* Determining Which Methods to Apply::
* Handling Invocation Errors::
@end menu
@node Determining Which Methods to Apply
@subsection Determining Which Methods to Apply
[ *fixme* Sorry - this is the area of GOOPS that I understand least of
all, so I'm afraid I have to pass on this section. Would some other
kind person consider filling it in? ]
@deffn generic apply-generic
@deffnx method apply-generic (gf <generic>) args
@end deffn
@deffn generic compute-applicable-methods
@deffnx method compute-applicable-methods (gf <generic>) args
@end deffn
@deffn generic sort-applicable-methods
@deffnx method sort-applicable-methods (gf <generic>) methods args
@end deffn
@deffn generic method-more-specific?
@deffnx method method-more-specific? (m1 <method>) (m2 <method>) args
@end deffn
@deffn generic apply-method
@deffnx method apply-method (gf <generic>) methods build-next args
@end deffn
@deffn generic apply-methods
@deffnx method apply-methods (gf <generic>) (l <list>) args
@end deffn
@node Handling Invocation Errors
@subsection Handling Invocation Errors
@deffn generic no-method
@deffnx method no-method (gf <generic>) args
When an application invokes a generic function, and no methods at all
have been defined for that generic function, GOOPS calls the
@code{no-method} generic function. The default method calls
@code{goops-error} with an appropriate message.
@end deffn
@deffn generic no-applicable-method
@deffnx method no-applicable-method (gf <generic>) args
When an application applies a generic function to a set of arguments,
and no methods have been defined for those argument types, GOOPS calls
the @code{no-applicable-method} generic function. The default method
calls @code{goops-error} with an appropriate message.
@end deffn
@deffn generic no-next-method
@deffnx method no-next-method (gf <generic>) args
When a generic function method calls @code{(next-method)} to invoke the
next less specialized method for that generic function, and no less
specialized methods have been defined for the current generic function
arguments, GOOPS calls the @code{no-next-method} generic function. The
default method calls @code{goops-error} with an appropriate message.
@end deffn
@node Redefining a Class
@section Redefining a Class
Suppose that a class @code{<my-class>} is defined using @code{define-class}
(@pxref{Class Definition,, define-class}), with slots that have
accessor functions, and that an application has created several instances
of @code{<my-class>} using @code{make} (@pxref{Instance Creation,,
make}). What then happens if @code{<my-class>} is redefined by calling
@code{define-class} again?
@menu
* Default Class Redefinition Behaviour::
* Customizing Class Redefinition::
@end menu
@node Default Class Redefinition Behaviour
@subsection Default Class Redefinition Behaviour
GOOPS' default answer to this question is as follows.
@itemize @bullet
@item
All existing direct instances of @code{<my-class>} are converted to be
instances of the new class. This is achieved by preserving the values
of slots that exist in both the old and new definitions, and
initializing the values of new slots in the usual way (@pxref{Instance
Creation,, make}).
@item
All existing subclasses of @code{<my-class>} are redefined, as though
the @code{define-class} expressions that defined them were re-evaluated
following the redefinition of @code{<my-class>}, and the class
redefinition process described here is applied recursively to the
redefined subclasses.
@item
Once all of its instances and subclasses have been updated, the class
metaobject previously bound to the variable @code{<my-class>} is no
longer needed and so can be allowed to be garbage collected.
@end itemize
To keep things tidy, GOOPS also needs to do a little housekeeping on
methods that are associated with the redefined class.
@itemize @bullet
@item
Slot accessor methods for slots in the old definition should be removed
from their generic functions. They will be replaced by accessor methods
for the slots of the new class definition.
@item
Any generic function method that uses the old @code{<my-class>} metaobject
as one of its formal parameter specializers must be updated to refer to
the new @code{<my-class>} metaobject. (Whenever a new generic function
method is defined, @code{define-method} adds the method to a list stored
in the class metaobject for each class used as a formal parameter
specializer, so it is easy to identify all the methods that must be
updated when a class is redefined.)
@end itemize
If this class redefinition strategy strikes you as rather counter-intuitive,
bear in mind that it is derived from similar behaviour in other object
systems such as CLOS, and that experience in those systems has shown it to be
very useful in practice.
Also bear in mind that, like most of GOOPS' default behaviour, it can
be customized@dots{}
@node Customizing Class Redefinition
@subsection Customizing Class Redefinition
When @code{define-class} notices that a class is being redefined,
it constructs the new class metaobject as usual, and then invokes the
@code{class-redefinition} generic function with the old and new classes
as arguments. Therefore, if the old or new classes have metaclasses
other than the default @code{<class>}, class redefinition behaviour can
be customized by defining a @code{class-redefinition} method that is
specialized for the relevant metaclasses.
@deffn generic class-redefinition
Handle the class redefinition from @var{old-class} to @var{new-class},
and return the new class metaobject that should be bound to the
variable specified by @code{define-class}'s first argument.
@end deffn
@deffn method class-redefinition (old-class <class>) (new-class <class>)
Implements GOOPS' default class redefinition behaviour, as described in
@ref{Default Class Redefinition Behaviour}. Returns the metaobject
for the new class definition.
@end deffn
An alternative class redefinition strategy could be to leave all
existing instances as instances of the old class, but accepting that the
old class is now ``nameless'', since its name has been taken over by the
new definition. In this strategy, any existing subclasses could also
be left as they are, on the understanding that they inherit from a nameless
superclass.
This strategy is easily implemented in GOOPS, by defining a new metaclass,
that will be used as the metaclass for all classes to which the strategy
should apply, and then defining a @code{class-redefinition} method that
is specialized for this metaclass:
@example
(define-class <can-be-nameless> (<class>))
(define-method (class-redefinition (old <can-be-nameless>)
(new <class>))
new)
@end example
When customization can be as easy as this, aren't you glad that GOOPS
implements the far more difficult strategy as its default!
Finally, note that, if @code{class-redefinition} itself is not customized,
the default @code{class-redefinition} method invokes three further
generic functions that could be individually customized:
@itemize @bullet
@item
(remove-class-accessors! @var{old-class})
@item
(update-direct-method! @var{method} @var{old-class} @var{new-class})
@item
(update-direct-subclass! @var{subclass} @var{old-class} @var{new-class})
@end itemize
and the default methods for these generic functions invoke further
generic functions, and so on@dots{} The detailed protocol for all of these
is described in @ref{MOP Specification}.
@node Changing the Class of an Instance
@section Changing the Class of an Instance
You can change the class of an existing instance by invoking the
generic function @code{change-class} with two arguments: the instance
and the new class.
@deffn generic change-class
@end deffn
The default method for @code{change-class} decides how to implement the
change of class by looking at the slot definitions for the instance's
existing class and for the new class. If the new class has slots with
the same name as slots in the existing class, the values for those slots
are preserved. Slots that are present only in the existing class are
discarded. Slots that are present only in the new class are initialized
using the corresponding slot definition's init function (@pxref{Classes,,
slot-init-function}).
@deffn {method} change-class (obj <object>) (new <class>)
Modify instance @var{obj} to make it an instance of class @var{new}.
The value of each of @var{obj}'s slots is preserved only if a similarly named
slot exists in @var{new}; any other slot values are discarded.
The slots in @var{new} that do not correspond to any of @var{obj}'s
pre-existing slots are initialized according to @var{new}'s slot definitions'
init functions.
@end deffn
Customized change of class behaviour can be implemented by defining
@code{change-class} methods that are specialized either by the class
of the instances to be modified or by the metaclass of the new class.
When a class is redefined (@pxref{Redefining a Class}), and the default
class redefinition behaviour is not overridden, GOOPS (eventually)
invokes the @code{change-class} generic function for each existing
instance of the redefined class.
@node Introspection
@section Introspection
@dfn{Introspection}, also known as @dfn{reflection}, is the name given
to the ability to obtain information dynamically about GOOPS metaobjects.
It is perhaps best illustrated by considering an object oriented language
that does not provide any introspection, namely C++.
Nothing in C++ allows a running program to obtain answers to the following
types of question:
@itemize @bullet
@item
What are the data members of this object or class?
@item
What classes does this class inherit from?
@item
Is this method call virtual or non-virtual?
@item
If I invoke @code{Employee::adjustHoliday()}, what class contains the
@code{adjustHoliday()} method that will be applied?
@end itemize
In C++, answers to such questions can only be determined by looking at
the source code, if you have access to it. GOOPS, on the other hand,
includes procedures that allow answers to these questions --- or their
GOOPS equivalents --- to be obtained dynamically, at run time.
@menu
* Classes::
* Slots::
* Instances::
* Built-in classes::
* Generic Functions::
* Generic Function Methods::
@end menu
@node Classes
@subsection Classes
@deffn {primitive procedure} class-name class
Return the name of class @var{class}.
This is the value of the @var{class} metaobject's @code{name} slot.
@end deffn
@deffn {primitive procedure} class-direct-supers class
Return a list containing the direct superclasses of @var{class}.
This is the value of the @var{class} metaobject's
@code{direct-supers} slot.
@end deffn
@deffn {primitive procedure} class-direct-slots class
Return a list containing the slot definitions of the direct slots of
@var{class}.
This is the value of the @var{class} metaobject's @code{direct-slots}
slot.
@end deffn
@deffn {primitive procedure} class-direct-subclasses class
Return a list containing the direct subclasses of @var{class}.
This is the value of the @var{class} metaobject's
@code{direct-subclasses} slot.
@end deffn
@deffn {primitive procedure} class-direct-methods class
Return a list of all the generic function methods that use @var{class}
as a formal parameter specializer.
This is the value of the @var{class} metaobject's @code{direct-methods}
slot.
@end deffn
@deffn {primitive procedure} class-precedence-list class
Return the class precedence list for class @var{class} (@pxref{Class
precedence list}).
This is the value of the @var{class} metaobject's @code{cpl} slot.
@end deffn
@deffn {primitive procedure} class-slots class
Return a list containing the slot definitions for all @var{class}'s slots,
including any slots that are inherited from superclasses.
This is the value of the @var{class} metaobject's @code{slots} slot.
@end deffn
@deffn {primitive procedure} class-environment class
Return the value of @var{class}'s @code{environment} slot.
[ *fixme* I don't know what this value is used for. ]
@end deffn
@deffn procedure class-subclasses class
Return a list of all subclasses of @var{class}.
@end deffn
@deffn procedure class-methods class
Return a list of all methods that use @var{class} or a subclass of
@var{class} as one of its formal parameter specializers.
@end deffn
@node Slots
@subsection Slots
@deffn procedure class-slot-definition class slot-name
Return the slot definition for the slot named @var{slot-name} in class
@var{class}. @var{slot-name} should be a symbol.
@end deffn
@deffn procedure slot-definition-name slot-def
Extract and return the slot name from @var{slot-def}.
@end deffn
@deffn procedure slot-definition-options slot-def
Extract and return the slot options from @var{slot-def}.
@end deffn
@deffn procedure slot-definition-allocation slot-def
Extract and return the slot allocation option from @var{slot-def}. This
is the value of the @code{#:allocation} keyword (@pxref{Slot Options,,
allocation}), or @code{#:instance} if the @code{#:allocation} keyword is
absent.
@end deffn
@deffn procedure slot-definition-getter slot-def
Extract and return the slot getter option from @var{slot-def}. This is
the value of the @code{#:getter} keyword (@pxref{Slot Options,,
getter}), or @code{#f} if the @code{#:getter} keyword is absent.
@end deffn
@deffn procedure slot-definition-setter slot-def
Extract and return the slot setter option from @var{slot-def}. This is
the value of the @code{#:setter} keyword (@pxref{Slot Options,,
setter}), or @code{#f} if the @code{#:setter} keyword is absent.
@end deffn
@deffn procedure slot-definition-accessor slot-def
Extract and return the slot accessor option from @var{slot-def}. This
is the value of the @code{#:accessor} keyword (@pxref{Slot Options,,
accessor}), or @code{#f} if the @code{#:accessor} keyword is absent.
@end deffn
@deffn procedure slot-definition-init-value slot-def
Extract and return the slot init-value option from @var{slot-def}. This
is the value of the @code{#:init-value} keyword (@pxref{Slot Options,,
init-value}), or the unbound value if the @code{#:init-value} keyword is
absent.
@end deffn
@deffn procedure slot-definition-init-form slot-def
Extract and return the slot init-form option from @var{slot-def}. This
is the value of the @code{#:init-form} keyword (@pxref{Slot Options,,
init-form}), or the unbound value if the @code{#:init-form} keyword is
absent.
@end deffn
@deffn procedure slot-definition-init-thunk slot-def
Extract and return the slot init-thunk option from @var{slot-def}. This
is the value of the @code{#:init-thunk} keyword (@pxref{Slot Options,,
init-thunk}), or @code{#f} if the @code{#:init-thunk} keyword is absent.
@end deffn
@deffn procedure slot-definition-init-keyword slot-def
Extract and return the slot init-keyword option from @var{slot-def}.
This is the value of the @code{#:init-keyword} keyword (@pxref{Slot
Options,, init-keyword}), or @code{#f} if the @code{#:init-keyword}
keyword is absent.
@end deffn
@deffn procedure slot-init-function class slot-name
Return the initialization function for the slot named @var{slot-name} in
class @var{class}. @var{slot-name} should be a symbol.
The returned initialization function incorporates the effects of the
standard @code{#:init-thunk}, @code{#:init-form} and @code{#:init-value}
slot options. These initializations can be overridden by the
@code{#:init-keyword} slot option or by a specialized @code{initialize}
method, so, in general, the function returned by
@code{slot-init-function} may be irrelevant. For a fuller discussion,
see @ref{Slot Options,, init-value}.
@end deffn
@node Instances
@subsection Instances
@deffn {primitive procedure} class-of value
Return the GOOPS class of any Scheme @var{value}.
@end deffn
@deffn {primitive procedure} instance? object
Return @code{#t} if @var{object} is any GOOPS instance, otherwise
@code{#f}.
@end deffn
@deffn procedure is-a? object class
Return @code{#t} if @var{object} is an instance of @var{class} or one of
its subclasses.
@end deffn
Implementation notes: @code{is-a?} uses @code{class-of} and
@code{class-precedence-list} to obtain the class precedence list for
@var{object}.
@node Built-in classes
@subsection Built-in classes
There are built-in classes like @code{<string>}, @code{<list>} and
@code{<number>} corresponding to all the Guile Scheme types. You can
use the @code{is-a?} predicate to ask whether any given value belongs to
a given class, or @code{class-of} to discover the class of a given
value.
@lisp
(is-a? 2.3 <number>) @result{} #t
(is-a? 2.3 <real>) @result{} #t
(is-a? 2.3 <string>) @result{} #f
(is-a? '("a" "b") <string>) @result{} #f
(is-a? '("a" "b") <list>) @result{} #t
(is-a? (car '("a" "b")) <string>) @result{} #t
(is-a? <string> <class>) @result{} #t
(is-a? <class> <string>) @result{} #f
(class-of 2.3) @result{} #<<class> <real> 908c708>
(class-of #(1 2 3)) @result{} #<<class> <vector> 908cd20>
(class-of <string>) @result{} #<<class> <class> 8bd3e10>
(class-of <class>) @result{} #<<class> <class> 8bd3e10>
@end lisp
@node Generic Functions
@subsection Generic Functions
@deffn {primitive procedure} generic-function-name gf
Return the name of generic function @var{gf}.
@end deffn
@deffn {primitive procedure} generic-function-methods gf
Return a list of the methods of generic function @var{gf}.
This is the value of the @var{gf} metaobject's @code{methods} slot.
@end deffn
@node Generic Function Methods
@subsection Generic Function Methods
@deffn {primitive procedure} method-generic-function method
Return the generic function that @var{method} belongs to.
This is the value of the @var{method} metaobject's
@code{generic-function} slot.
@end deffn
@deffn {primitive procedure} method-specializers method
Return a list of @var{method}'s formal parameter specializers .
This is the value of the @var{method} metaobject's
@code{specializers} slot.
@end deffn
@deffn {primitive procedure} method-procedure method
Return the procedure that implements @var{method}.
This is the value of the @var{method} metaobject's
@code{procedure} slot.
@end deffn
@deffn generic method-source
@deffnx method method-source (m <method>)
Return an expression that prints to show the definition of method
@var{m}.
@example
(define-generic cube)
(define-method (cube (n <number>))
(* n n n))
(map method-source (generic-function-methods cube))
@result{}
((method ((n <number>)) (* n n n)))
@end example
@end deffn
@node GOOPS Error Handling
@section Error Handling
The procedure @code{goops-error} is called to raise an appropriate error
by the default methods of the following generic functions:
@itemize @bullet
@item
@code{slot-missing} (@pxref{Handling Slot Access Errors,, slot-missing})
@item
@code{slot-unbound} (@pxref{Handling Slot Access Errors,, slot-unbound})
@item
@code{no-method} (@pxref{Handling Invocation Errors,, no-method})
@item
@code{no-applicable-method} (@pxref{Handling Invocation Errors,,
no-applicable-method})
@item
@code{no-next-method} (@pxref{Handling Invocation Errors,,
no-next-method})
@end itemize
If you customize these functions for particular classes or metaclasses,
you may still want to use @code{goops-error} to signal any error
conditions that you detect.
@deffn procedure goops-error format-string . args
Raise an error with key @code{goops-error} and error message constructed
from @var{format-string} and @var{args}. Error message formatting is
as done by @code{scm-error}.
@end deffn
@node Object Comparisons
@section Object Comparisons
@deffn generic eqv?
@deffnx method eqv? ((x <top>) (y <top>))
@deffnx generic equal?
@deffnx method equal? ((x <top>) (y <top>))
@deffnx generic =
@deffnx method = ((x <number>) (y <number>))
Generic functions and default (unspecialized) methods for comparing two
GOOPS objects.
The default method for @code{eqv?} returns @code{#t} for all values
that are equal in the sense defined by R5RS and the Guile reference
manual, otherwise @code{#f}. The default method for @code{equal?}
returns @code{#t} or @code{#f} in the sense defined by R5RS and the
Guile reference manual. If no such comparison is defined,
@code{equal?} returns the result of a call to @code{eqv?}. The
default method for = returns @code{#t} if @var{x} and @var{y} are
numerically equal, otherwise @code{#f}.
Application class authors may wish to define specialized methods for
@code{eqv?}, @code{equal?} and @code{=} that compare instances of the
same class for equality in whatever sense is useful to the
application. Such methods will only be called if the arguments have
the same class and the result of the comparison isn't defined by R5RS
and the Guile reference manual.
@end deffn
@node Cloning Objects
@section Cloning Objects
@deffn generic shallow-clone
@deffnx method shallow-clone (self <object>)
Return a ``shallow'' clone of @var{self}. The default method makes a
shallow clone by allocating a new instance and copying slot values from
self to the new instance. Each slot value is copied either as an
immediate value or by reference.
@end deffn
@deffn generic deep-clone
@deffnx method deep-clone (self <object>)
Return a ``deep'' clone of @var{self}. The default method makes a deep
clone by allocating a new instance and copying or cloning slot values
from self to the new instance. If a slot value is an instance
(satisfies @code{instance?}), it is cloned by calling @code{deep-clone}
on that value. Other slot values are copied either as immediate values
or by reference.
@end deffn
@node Write and Display
@section Write and Display
@deffn {primitive generic} write object port
@deffnx {primitive generic} display object port
When GOOPS is loaded, @code{write} and @code{display} become generic
functions with special methods for printing
@itemize @bullet
@item
objects - instances of the class @code{<object>}
@item
foreign objects - instances of the class @code{<foreign-object>}
@item
classes - instances of the class @code{<class>}
@item
generic functions - instances of the class @code{<generic>}
@item
methods - instances of the class @code{<method>}.
@end itemize
@code{write} and @code{display} print non-GOOPS values in the same way
as the Guile primitive @code{write} and @code{display} functions.
@end deffn
@node The Metaobject Protocol
@section The Metaobject Protocol
GOOPS is based on a ``metaobject protocol'' (aka ``MOP'') derived from
the ones used in CLOS (the Common Lisp Object System), tiny-clos (a
small Scheme implementation of a subset of CLOS functionality) and
STKlos.
GOOPS can be used by application authors at a basic level without any
need to understand what the MOP is and how it works. On the other hand,
the MOP underlies even very simple customizations --- such as defining
an @code{initialize} method to customize the initialization of instances
of an application-defined class --- and an understanding of the MOP
makes it much easier to explain such customizations in a precise way.
And in the long run, understanding the MOP is the key both to
understanding GOOPS at a deeper level and to taking full advantage of
GOOPS' power, by customizing the behaviour of GOOPS itself.
@menu
* Metaobjects and the Metaobject Protocol::
* Terminology::
* MOP Specification::
* Class Definition Internals::
* Customizing Class Definition::
* Customizing Instance Creation::
* Class Redefinition::
* Method Definition::
* Method Definition Internals::
* Generic Function Internals::
* Generic Function Invocation::
@end menu
@node Metaobjects and the Metaobject Protocol
@subsection Metaobjects and the Metaobject Protocol
The building blocks of GOOPS are classes, slot definitions, instances,
generic functions and methods. A class is a grouping of inheritance
relations and slot definitions. An instance is an object with slots
that are allocated following the rules implied by its class's
superclasses and slot definitions. A generic function is a collection
of methods and rules for determining which of those methods to apply
when the generic function is invoked. A method is a procedure and a set
of specializers that specify the type of arguments to which the
procedure is applicable.
Of these entities, GOOPS represents classes, generic functions and
methods as ``metaobjects''. In other words, the values in a GOOPS
program that describe classes, generic functions and methods, are
themselves instances (or ``objects'') of special GOOPS classes that
encapsulate the behaviour, respectively, of classes, generic functions,
and methods.
(The other two entities are slot definitions and instances. Slot
definitions are not strictly instances, but every slot definition is
associated with a GOOPS class that specifies the behaviour of the slot
as regards accessibility and protection from garbage collection.
Instances are of course objects in the usual sense, and there is no
benefit from thinking of them as metaobjects.)
The ``metaobject protocol'' (aka ``MOP'') is the specification of the
generic functions which determine the behaviour of these metaobjects and
the circumstances in which these generic functions are invoked.
For a concrete example of what this means, consider how GOOPS calculates
the set of slots for a class that is being defined using
@code{define-class}. The desired set of slots is the union of the new
class's direct slots and the slots of all its superclasses. But
@code{define-class} itself does not perform this calculation. Instead,
there is a method of the @code{initialize} generic function that is
specialized for instances of type @code{<class>}, and it is this method
that performs the slot calculation.
@code{initialize} is a generic function which GOOPS calls whenever a new
instance is created, immediately after allocating memory for a new
instance, in order to initialize the new instance's slots. The sequence
of steps is as follows.
@itemize @bullet
@item
@code{define-class} uses @code{make} to make a new instance of the
@code{<class>} class, passing as initialization arguments the
superclasses, slot definitions and class options that were specified in
the @code{define-class} form.
@item
@code{make} allocates memory for the new instance, and then invokes the
@code{initialize} generic function to initialize the new instance's
slots.
@item
The @code{initialize} generic function applies the method that is
specialized for instances of type @code{<class>}, and this method
performs the slot calculation.
@end itemize
In other words, rather than being hardcoded in @code{define-class}, the
behaviour of class definition is encapsulated by generic function
methods that are specialized for the class @code{<class>}.
It is possible to create a new class that inherits from @code{<class>},
which is called a ``metaclass'', and to write a new @code{initialize}
method that is specialized for instances of the new metaclass. Then, if
the @code{define-class} form includes a @code{#:metaclass} class option
whose value is the new metaclass, the class that is defined by the
@code{define-class} form will be an instance of the new metaclass rather
than of the default @code{<class>}, and will be defined in accordance
with the new @code{initialize} method. Thus the default slot
calculation, as well as any other aspect of the new class's relationship
with its superclasses, can be modified or overridden.
In a similar way, the behaviour of generic functions can be modified or
overridden by creating a new class that inherits from the standard
generic function class @code{<generic>}, writing appropriate methods
that are specialized to the new class, and creating new generic
functions that are instances of the new class.
The same is true for method metaobjects. And the same basic mechanism
allows the application class author to write an @code{initialize} method
that is specialized to their application class, to initialize instances
of that class.
Such is the power of the MOP. Note that @code{initialize} is just one
of a large number of generic functions that can be customized to modify
the behaviour of application objects and classes and of GOOPS itself.
Each following section covers a particular area of GOOPS functionality,
and describes the generic functions that are relevant for customization
of that area.
@node Terminology
@subsection Terminology
It is assumed that the reader is already familiar with standard object
orientation concepts such as classes, objects/instances,
inheritance/subclassing, generic functions and methods, encapsulation
and polymorphism.
This section explains some of the less well known concepts and
terminology that GOOPS uses, which are assumed by the following sections
of the reference manual.
@subsubheading Metaclass
A @dfn{metaclass} is the class of an object which represents a GOOPS
class. Put more succinctly, a metaclass is a class's class.
Most GOOPS classes have the metaclass @code{<class>} and, by default,
any new class that is created using @code{define-class} has the
metaclass @code{<class>}.
But what does this really mean? To find out, let's look in more detail
at what happens when a new class is created using @code{define-class}:
@example
(define-class <my-class> (<object>) . slots)
@end example
GOOPS actually expands the @code{define-class} form to something like
this
@example
(define <my-class> (class (<object>) . slots))
@end example
and thence to
@example
(define <my-class>
(make <class> #:supers (list <object>) #:slots slots))
@end example
In other words, the value of @code{<my-class>} is in fact an instance of
the class @code{<class>} with slot values specifying the superclasses
and slot definitions for the class @code{<my-class>}. (@code{#:supers}
and @code{#:slots} are initialization keywords for the @code{dsupers}
and @code{dslots} slots of the @code{<class>} class.)
In order to take advantage of the full power of the GOOPS metaobject
protocol (@pxref{MOP Specification}), it is sometimes desirable to
create a new class with a metaclass other than the default
@code{<class>}. This is done by writing:
@example
(define-class <my-class2> (<object>)
slot @dots{}
#:metaclass <my-metaclass>)
@end example
GOOPS expands this to something like:
@example
(define <my-class2>
(make <my-metaclass> #:supers (list <object>) #:slots slots))
@end example
In this case, the value of @code{<my-class2>} is an instance of the more
specialized class @code{<my-metaclass>}. Note that
@code{<my-metaclass>} itself must previously have been defined as a
subclass of @code{<class>}. For a full discussion of when and how it is
useful to define new metaclasses, see @ref{MOP Specification}.
Now let's make an instance of @code{<my-class2>}:
@example
(define my-object (make <my-class2> ...))
@end example
All of the following statements are correct expressions of the
relationships between @code{my-object}, @code{<my-class2>},
@code{<my-metaclass>} and @code{<class>}.
@itemize @bullet
@item
@code{my-object} is an instance of the class @code{<my-class2>}.
@item
@code{<my-class2>} is an instance of the class @code{<my-metaclass>}.
@item
@code{<my-metaclass>} is an instance of the class @code{<class>}.
@item
The class of @code{my-object} is @code{<my-class2>}.
@item
The metaclass of @code{my-object} is @code{<my-metaclass>}.
@item
The class of @code{<my-class2>} is @code{<my-metaclass>}.
@item
The metaclass of @code{<my-class2>} is @code{<class>}.
@item
The class of @code{<my-metaclass>} is @code{<class>}.
@item
The metaclass of @code{<my-metaclass>} is @code{<class>}.
@item
@code{<my-class2>} is not a metaclass, since it is does not inherit from
@code{<class>}.
@item
@code{<my-metaclass>} is a metaclass, since it inherits from
@code{<class>}.
@end itemize
@subsubheading Class Precedence List
The @dfn{class precedence list} of a class is the list of all direct and
indirect superclasses of that class, including the class itself.
In the absence of multiple inheritance, the class precedence list is
ordered straightforwardly, beginning with the class itself and ending
with @code{<top>}.
For example, given this inheritance hierarchy:
@example
(define-class <invertebrate> (<object>) @dots{})
(define-class <echinoderm> (<invertebrate>) @dots{})
(define-class <starfish> (<echinoderm>) @dots{})
@end example
the class precedence list of <starfish> would be
@example
(<starfish> <echinoderm> <invertebrate> <object> <top>)
@end example
With multiple inheritance, the algorithm is a little more complicated.
A full description is provided by the GOOPS Tutorial: see @ref{Class
precedence list}.
``Class precedence list'' is often abbreviated, in documentation and
Scheme variable names, to @dfn{cpl}.
@subsubheading Accessor
An @dfn{accessor} is a generic function with both reference and setter
methods.
@example
(define-accessor perimeter)
@end example
Reference methods for an accessor are defined in the same way as generic
function methods.
@example
(define-method (perimeter (s <square>))
(* 4 (side-length s)))
@end example
Setter methods for an accessor are defined by specifying ``(setter
<accessor-name>)'' as the first parameter of the @code{define-method}
call.
@example
(define-method ((setter perimeter) (s <square>) (n <number>))
(set! (side-length s) (/ n 4)))
@end example
Once an appropriate setter method has been defined in this way, it can
be invoked using the generalized @code{set!} syntax, as in:
@example
(set! (perimeter s1) 18.3)
@end example
@node MOP Specification
@subsection MOP Specification
The aim of the MOP specification in this chapter is to specify all the
customizable generic function invocations that can be made by the standard
GOOPS syntax, procedures and methods, and to explain the protocol for
customizing such invocations.
A generic function invocation is customizable if the types of the arguments
to which it is applied are not all determined by the lexical context in
which the invocation appears. For example,
@itemize @bullet
@item
the @code{(initialize @var{instance} @var{initargs})} invocation in the
default @code{make-instance} method is customizable, because the type of the
@code{@var{instance}} argument is determined by the class that was passed to
@code{make-instance}.
@item
the @code{(make <generic> #:name ',name)} invocation in @code{define-generic}
is not customizable, because all of its arguments have lexically determined
types.
@end itemize
When using this rule to decide whether a given generic function invocation
is customizable, we ignore arguments that are expected to be handled in
method definitions as a single ``rest'' list argument.
For each customizable generic function invocation, the @dfn{invocation
protocol} is explained by specifying
@itemize @bullet
@item
what, conceptually, the applied method is intended to do
@item
what assumptions, if any, the caller makes about the applied method's side
effects
@item
what the caller expects to get as the applied method's return value.
@end itemize
@node Class Definition Internals
@subsection Class Definition Internals
@code{define-class} (syntax)
@itemize @bullet
@item
@code{class} (syntax)
@itemize @bullet
@item
@code{make-class} (procedure)
@itemize @bullet
@item
@code{make @var{metaclass} @dots{}} (generic)
@var{metaclass} is the metaclass of the class being defined, either
taken from the @code{#:metaclass} class option or computed by
@code{ensure-metaclass}. The applied method must create and return the
fully initialized class metaobject for the new class definition.
@end itemize
@end itemize
@item
@code{class-redefinition @var{old-class} @var{new-class}} (generic)
@code{define-class} calls @code{class-redefinition} if the variable
specified by its first argument already held a GOOPS class definition.
@var{old-class} and @var{new-class} are the old and new class metaobjects.
The applied method should perform whatever is necessary to handle the
redefinition, and should return the class metaobject that is to be bound
to @code{define-class}'s variable. The default class redefinition
protocol is described in @ref{Class Redefinition}.
@end itemize
The @code{(make @var{metaclass} @dots{})} invocation above will create
an class metaobject with metaclass @var{metaclass}. By default, this
metaobject will be initialized by the @code{initialize} method that is
specialized for instances of type @code{<class>}.
@code{initialize <class> @var{initargs}} (method)
@itemize @bullet
@item
@code{compute-cpl @var{class}} (generic)
The applied method should compute and return the class precedence list
for @var{class} as a list of class metaobjects. When @code{compute-cpl}
is called, the following @var{class} metaobject slots have all been
initialized: @code{name}, @code{direct-supers}, @code{direct-slots},
@code{direct-subclasses} (empty), @code{direct-methods}. The value
returned by @code{compute-cpl} will be stored in the @code{cpl} slot.
@item
@code{compute-slots @var{class}} (generic)
The applied method should compute and return the slots (union of direct
and inherited) for @var{class} as a list of slot definitions. When
@code{compute-slots} is called, all the @var{class} metaobject slots
mentioned for @code{compute-cpl} have been initialized, plus the
following: @code{cpl}, @code{redefined} (@code{#f}), @code{environment}.
The value returned by @code{compute-slots} will be stored in the
@code{slots} slot.
@item
@code{compute-get-n-set @var{class} @var{slot-def}} (generic)
@code{initialize} calls @code{compute-get-n-set} for each slot computed
by @code{compute-slots}. The applied method should compute and return a
pair of closures that, respectively, get and set the value of the specified
slot. The get closure should have arity 1 and expect a single argument
that is the instance whose slot value is to be retrieved. The set closure
should have arity 2 and expect two arguments, where the first argument is
the instance whose slot value is to be set and the second argument is the
new value for that slot. The closures should be returned in a two element
list: @code{(list @var{get} @var{set})}.
The closures returned by @code{compute-get-n-set} are stored as part of
the value of the @var{class} metaobject's @code{getters-n-setters} slot.
Specifically, the value of this slot is a list with the same number of
elements as there are slots in the class, and each element looks either like
@example
@code{(@var{slot-name-symbol} @var{init-function} . @var{index})}
@end example
or like
@example
@code{(@var{slot-name-symbol} @var{init-function} @var{get} @var{set})}
@end example
Where the get and set closures are replaced by @var{index}, the slot is
an instance slot and @var{index} is the slot's index in the underlying
structure: GOOPS knows how to get and set the value of such slots and so
does not need specially constructed get and set closures. Otherwise,
@var{get} and @var{set} are the closures returned by @code{compute-get-n-set}.
The structure of the @code{getters-n-setters} slot value is important when
understanding the next customizable generic functions that @code{initialize}
calls@dots{}
@item
@code{compute-getter-method @var{class} @var{gns}} (generic)
@code{initialize} calls @code{compute-getter-method} for each of the class's
slots (as determined by @code{compute-slots}) that includes a
@code{#:getter} or @code{#:accessor} slot option. @var{gns} is the
element of the @var{class} metaobject's @code{getters-n-setters} slot that
specifies how the slot in question is referenced and set, as described
above under @code{compute-get-n-set}. The applied method should create
and return a method that is specialized for instances of type @var{class}
and uses the get closure to retrieve the slot's value. [ *fixme Need
to insert something here about checking that the value is not unbound. ]
@code{initialize} uses @code{add-method!} to add the returned method to
the generic function named by the slot definition's @code{#:getter} or
@code{#:accessor} option.
@item
@code{compute-setter-method @var{class} @var{gns}} (generic)
@code{compute-setter-method} is invoked with the same arguments as
@code{compute-getter-method}, for each of the class's slots that includes
a @code{#:setter} or @code{#:accessor} slot option. The applied method
should create and return a method that is specialized for instances of
type @var{class} and uses the set closure to set the slot's value.
@code{initialize} then uses @code{add-method!} to add the returned method
to the generic function named by the slot definition's @code{#:setter}
or @code{#:accessor} option.
@end itemize
@code{define-class} expands to an expression which
@itemize @bullet
@item
checks that it is being evaluated only at top level
@item
defines any accessors that are implied by the @var{slot-definition}s
@item
uses @code{class} to create the new class (@pxref{Class Definition
Internals,, class})
@item
checks for a previous class definition for @var{name} and, if found,
handles the redefinition by invoking @code{class-redefinition}
(@pxref{Redefining a Class}).
@end itemize
@deffn syntax class name (super @dots{}) slot-definition @dots{} . options
Return a newly created class that inherits from @var{super}s, with
direct slots defined by @var{slot-definition}s and class options
@var{options}. For the format of @var{slot-definition}s and
@var{options}, see @ref{Class Definition,, define-class}.
@end deffn
@noindent @code{class} expands to an expression which
@itemize @bullet
@item
processes the class and slot definition options to check that they are
well-formed, to convert the @code{#:init-form} option to an
@code{#: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
@item
calls @code{make-class} to create the class with the processed and
evaluated parameters.
@end itemize
@deffn procedure make-class supers slots . options
Return a newly created class that inherits from @var{supers}, with
direct slots defined by @var{slots} and class options @var{options}.
For the format of @var{slots} and @var{options}, see @ref{Class
Definition,, define-class}, except note that for @code{make-class},
@var{slots} and @var{options} are separate list parameters: @var{slots}
here is a list of slot definitions.
@end deffn
@noindent @code{make-class}
@itemize @bullet
@item
adds @code{<object>} to the @var{supers} list if @var{supers} is empty
or if none of the classes in @var{supers} have @code{<object>} in their
class precedence list
@item
defaults the @code{#:environment}, @code{#:name} and @code{#:metaclass}
options, if they are not specified by @var{options}, to the current
top-level environment, the unbound value, and @code{(ensure-metaclass
@var{supers})} respectively (@pxref{Class Definition Internals,,
ensure-metaclass})
@item
checks for duplicate classes in @var{supers} and duplicate slot names in
@var{slots}, and signals an error if there are any duplicates
@item
calls @code{make}, passing the metaclass as the first parameter and all
other parameters as option keywords with values.
@end itemize
@deffn procedure ensure-metaclass supers env
Return a metaclass suitable for a class that inherits from the list of
classes in @var{supers}. The returned metaclass is the union by
inheritance of the metaclasses of the classes in @var{supers}.
In the simplest case, where all the @var{supers} are straightforward
classes with metaclass @code{<class>}, the returned metaclass is just
@code{<class>}.
For a more complex example, suppose that @var{supers} contained one
class with metaclass @code{<operator-class>} and one with metaclass
@code{<foreign-object-class>}. Then the returned metaclass would be a
class that inherits from both @code{<operator-class>} and
@code{<foreign-object-class>}.
If @var{supers} is the empty list, @code{ensure-metaclass} returns the
default GOOPS metaclass @code{<class>}.
GOOPS keeps a list of the metaclasses created by
@code{ensure-metaclass}, so that each required type of metaclass only
has to be created once.
The @code{env} parameter is ignored.
@end deffn
@deffn procedure ensure-metaclass-with-supers meta-supers
@code{ensure-metaclass-with-supers} is an internal procedure used by
@code{ensure-metaclass} (@pxref{Class Definition Internals,,
ensure-metaclass}). It returns a metaclass that is the union by
inheritance of the metaclasses in @var{meta-supers}.
@end deffn
The internals of @code{make}, which is ultimately used to create the new
class object, are described in @ref{Customizing Instance Creation},
which covers the creation and initialization of instances in general.
@node Customizing Class Definition
@subsection Customizing Class Definition
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
@itemize @bullet
@item
(initialize @var{class} @dots{})
@end itemize
where @var{class} is the newly allocated class instance, and the default
@code{initialize} method for arguments of type @code{<class>} calls the
generic functions
@itemize @bullet
@item
(compute-cpl @var{class})
@item
(compute-slots @var{class})
@item
(compute-get-n-set @var{class} @var{slot-def}), for each of the slot
definitions returned by @code{compute-slots}
@item
(compute-getter-method @var{class} @var{slot-def}), for each of the
slot definitions returned by @code{compute-slots} that includes a
@code{#:getter} or @code{#:accessor} slot option
@item
(compute-setter-method @var{class} @var{slot-def}), for each of the
slot definitions returned by @code{compute-slots} that includes a
@code{#:setter} or @code{#:accessor} slot option.
@end itemize
If the metaclass of the new class is something more specialized than the
default @code{<class>}, then the type of @var{class} in the calls above
is more specialized than @code{<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
@code{initialize}, @code{compute-cpl} or @code{compute-get-n-set}.
@code{compute-cpl} computes the class precedence list (``CPL'') for the
new class (@pxref{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 @code{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 @code{compute-std-cpl}
procedure, which is in turn called by the default @code{compute-cpl}
method.
@deffn procedure compute-std-cpl class
Compute and return the class precedence list for @var{class} according
to the algorithm described in @ref{Class precedence list}.
@end deffn
@code{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 @code{define-class} form, plus the slot definitions
that are inherited from the new class's superclasses. The default
@code{compute-slots} method uses the CPL computed by @code{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 @code{compute-slots} would be
to implement an alternative resolution strategy for slot name conflicts.
@code{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 @code{compute-get-n-set} method, specialized for classes of
type @code{<class>}, handles the standard GOOPS values for the
@code{#:allocation} slot option (@pxref{Slot Options,, allocation}). By
defining a new @code{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.
@example
(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>)
@end example
The usage of @code{compute-getter-method} and @code{compute-setter-method}
is described in @ref{MOP Specification}.
@code{compute-cpl} and @code{compute-get-n-set} are called by the
standard @code{initialize} method for classes whose metaclass is
@code{<class>}. But @code{initialize} itself can also be modified, by
defining an @code{initialize} method specialized to the new class's
metaclass. Such a method could complete override the standard
behaviour, by not calling @code{(next-method)} at all, but more
typically it would perform additional class initialization steps before
and/or after calling @code{(next-method)} for the standard behaviour.
@node Customizing Instance Creation
@subsection Customizing Instance Creation
@code{make <class> . @var{initargs}} (method)
@itemize @bullet
@item
@code{allocate-instance @var{class} @var{initargs}} (generic)
The applied @code{allocate-instance} method should allocate storage for
a new instance of class @var{class} and return the uninitialized instance.
@item
@code{initialize @var{instance} @var{initargs}} (generic)
@var{instance} is the uninitialized instance returned by
@code{allocate-instance}. The applied method should initialize the new
instance in whatever sense is appropriate for its class. The method's
return value is ignored.
@end itemize
@code{make} itself is a generic function. Hence the @code{make}
invocation itself can be customized in the case where the new instance's
metaclass is more specialized than the default @code{<class>}, by
defining a @code{make} method that is specialized to that metaclass.
Normally, however, the method for classes with metaclass @code{<class>}
will be applied. This method calls two generic functions:
@itemize @bullet
@item
(allocate-instance @var{class} . @var{initargs})
@item
(initialize @var{instance} . @var{initargs})
@end itemize
@code{allocate-instance} allocates storage for and returns the new
instance, uninitialized. You might customize @code{allocate-instance},
for example, if you wanted to provide a GOOPS wrapper around some other
object programming system.
To do this, you would create a specialized metaclass, which would act as
the metaclass for all classes and instances from the other system. Then
define an @code{allocate-instance} method, specialized to that
metaclass, which calls a Guile primitive C function, which in turn
allocates the new instance using the interface of the other object
system.
In this case, for a complete system, you would also need to customize a
number of other generic functions like @code{make} and
@code{initialize}, so that GOOPS knows how to make classes from the
other system, access instance slots, and so on.
@code{initialize} initializes the instance that is returned by
@code{allocate-instance}. The standard GOOPS methods perform
initializations appropriate to the instance class.
@itemize @bullet
@item
At the least specialized level, the method for instances of type
@code{<object>} performs internal GOOPS instance initialization, and
initializes the instance's slots according to the slot definitions and
any slot initialization keywords that appear in @var{initargs}.
@item
The method for instances of type @code{<class>} calls
@code{(next-method)}, then performs the class initializations described
in @ref{Customizing Class Definition}.
@item
and so on for generic functions, method, operator classes @dots{}
@end itemize
Similarly, you can customize the initialization of instances of any
application-defined class by defining an @code{initialize} method
specialized to that class.
Imagine a class whose instances' slots need to be initialized at
instance creation time by querying a database. Although it might be
possible to achieve this a combination of @code{#:init-thunk} keywords
and closures in the slot definitions, it is neater to write an
@code{initialize} method for the class that queries the database once
and initializes all the dependent slot values according to the results.
@node Class Redefinition
@subsection Class Redefinition
The default @code{class-redefinition} method, specialized for classes
with the default metaclass @code{<class>}, has the following internal
protocol.
@code{class-redefinition (@var{old <class>}) (@var{new <class>})}
(method)
@itemize @bullet
@item
@code{remove-class-accessors! @var{old}} (generic)
@item
@code{update-direct-method! @var{method} @var{old} @var{new}} (generic)
@item
@code{update-direct-subclass! @var{subclass} @var{old} @var{new}} (generic)
@end itemize
This protocol cleans up things that the definition of the old class
once changed and modifies things to work with the new class.
The default @code{remove-class-accessors!} method removes the
accessor methods of the old class from all classes which they
specialize.
The default @code{update-direct-method!} method substitutes the new
class for the old in all methods specialized to the old class.
The default @code{update-direct-subclass!} method invokes
@code{class-redefinition} recursively to handle the redefinition of
subclasses.
When a class is redefined, any existing instance of the redefined class
will be modified for the new class definition before the next time that
any of the instance's slot is referenced or set. GOOPS modifies each
instance by calling the generic function @code{change-class}.
The default @code{change-class} method copies slot values from the old
to the modified instance, and initializes new slots, as described in
@ref{Changing the Class of an Instance}. After doing so, it makes a
generic function invocation that can be used to customize the instance
update algorithm.
@code{change-class (@var{old-instance <object>}) (@var{new <class>})} (method)
@itemize @bullet
@item
@code{update-instance-for-different-class @var{old-instance} @var{new-instance}} (generic)
@code{change-class} invokes @code{update-instance-for-different-class}
as the last thing that it does before returning. The applied method can
make any further adjustments to @var{new-instance} that are required to
complete or modify the change of class. The return value from the
applied method is ignored.
The default @code{update-instance-for-different-class} method does
nothing.
@end itemize
@node Method Definition
@subsection Method Definition
@code{define-method} (syntax)
@itemize @bullet
@item
@code{add-method! @var{target} @var{method}} (generic)
@code{define-method} invokes the @code{add-method!} generic function to
handle adding the new method to a variety of possible targets. GOOPS
includes methods to handle @var{target} as
@itemize @bullet
@item
a generic function (the most common case)
@item
a procedure
@item
a primitive generic (@pxref{Extending Primitives})
@end itemize
By defining further methods for @code{add-method!}, you can
theoretically handle adding methods to further types of target.
@end itemize
@node Method Definition Internals
@subsection Method Definition Internals
@code{define-method}
@itemize @bullet
@item
checks the form of the first parameter, and applies the following steps
to the accessor's setter if it has the @code{(setter @dots{})} form
@item
interpolates a call to @code{define-generic} or @code{define-accessor}
if a generic function is not already defined with the supplied name
@item
calls @code{method} with the @var{parameter}s and @var{body}, to make a
new method instance
@item
calls @code{add-method!} to add this method to the relevant generic
function.
@end itemize
@deffn syntax method (parameter @dots{}) . body
Make a method whose specializers are defined by the classes in
@var{parameter}s and whose procedure definition is constructed from the
@var{parameter} symbols and @var{body} forms.
The @var{parameter} and @var{body} parameters should be as for
@code{define-method} (@pxref{Adding Methods to Generic Functions,,
define-method}).
@end deffn
@code{method}
@itemize @bullet
@item
extracts formals and specializing classes from the @var{parameter}s,
defaulting the class for unspecialized parameters to @code{<top>}
@item
creates a closure using the formals and the @var{body} forms
@item
calls @code{make} with metaclass @code{<method>} and the specializers
and closure using the @code{#:specializers} and @code{#:procedure}
keywords.
@end itemize
@deffn procedure make-method specializers procedure
Make a method using @var{specializers} and @var{procedure}.
@var{specializers} should be a list of classes that specifies the
parameter combinations to which this method will be applicable.
@var{procedure} should be the closure that will applied to the generic
function parameters when this method is invoked.
@end deffn
@code{make-method} is a simple wrapper around @code{make} with metaclass
@code{<method>}.
@deffn generic add-method! target method
Generic function for adding method @var{method} to @var{target}.
@end deffn
@deffn method add-method! (generic <generic>) (method <method>)
Add method @var{method} to the generic function @var{generic}.
@end deffn
@deffn method add-method! (proc <procedure>) (method <method>)
If @var{proc} is a procedure with generic capability (@pxref{Extending
Primitives,, generic-capability?}), upgrade it to a primitive generic
and add @var{method} to its generic function definition.
@end deffn
@deffn method add-method! (pg <primitive-generic>) (method <method>)
Add method @var{method} to the generic function definition of @var{pg}.
Implementation: @code{(add-method! (primitive-generic-generic pg) method)}.
@end deffn
@deffn method add-method! (whatever <top>) (method <method>)
Raise an error indicating that @var{whatever} is not a valid generic
function.
@end deffn
@node Generic Function Internals
@subsection Generic Function Internals
@code{define-generic} calls @code{ensure-generic} to upgrade a
pre-existing procedure value, or @code{make} with metaclass
@code{<generic>} to create a new generic function.
@code{define-accessor} calls @code{ensure-accessor} to upgrade a
pre-existing procedure value, or @code{make-accessor} to create a new
accessor.
@deffn procedure ensure-generic old-definition [name]
Return a generic function with name @var{name}, if possible by using or
upgrading @var{old-definition}. If unspecified, @var{name} defaults to
@code{#f}.
If @var{old-definition} is already a generic function, it is returned
unchanged.
If @var{old-definition} is a Scheme procedure or procedure-with-setter,
@code{ensure-generic} returns a new generic function that uses
@var{old-definition} for its default procedure and setter.
Otherwise @code{ensure-generic} returns a new generic function with no
defaults and no methods.
@end deffn
@deffn procedure make-generic [name]
Return a new generic function with name @code{(car @var{name})}. If
unspecified, @var{name} defaults to @code{#f}.
@end deffn
@code{ensure-generic} calls @code{make} with metaclasses
@code{<generic>} and @code{<generic-with-setter>}, depending on the
previous value of the variable that it is trying to upgrade.
@code{make-generic} is a simple wrapper for @code{make} with metaclass
@code{<generic>}.
@deffn procedure ensure-accessor proc [name]
Return an accessor with name @var{name}, if possible by using or
upgrading @var{proc}. If unspecified, @var{name} defaults to @code{#f}.
If @var{proc} is already an accessor, it is returned unchanged.
If @var{proc} is a Scheme procedure, procedure-with-setter or generic
function, @code{ensure-accessor} returns an accessor that reuses the
reusable elements of @var{proc}.
Otherwise @code{ensure-accessor} returns a new accessor with no defaults
and no methods.
@end deffn
@deffn procedure make-accessor [name]
Return a new accessor with name @code{(car @var{name})}. If
unspecified, @var{name} defaults to @code{#f}.
@end deffn
@code{ensure-accessor} calls @code{make} with
metaclass @code{<generic-with-setter>}, as well as calls to
@code{ensure-generic}, @code{make-accessor} and (tail recursively)
@code{ensure-accessor}.
@code{make-accessor} calls @code{make} twice, first
with metaclass @code{<generic>} to create a generic function for the
setter, then with metaclass @code{<generic-with-setter>} to create the
accessor, passing the setter generic function as the value of the
@code{#:setter} keyword.
@node Generic Function Invocation
@subsection Generic Function Invocation
[ *fixme* Description required here. ]
@code{apply-generic}
@itemize @bullet
@item
@code{no-method}
@item
@code{compute-applicable-methods}
@item
@code{sort-applicable-methods}
@item
@code{apply-methods}
@item
@code{no-applicable-method}
@end itemize
@code{sort-applicable-methods}
@itemize @bullet
@item
@code{method-more-specific?}
@end itemize
@code{apply-methods}
@itemize @bullet
@item
@code{apply-method}
@end itemize
@code{next-method}
@itemize @bullet
@item
@code{no-next-method}
@end itemize