mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-05-03 05:20:16 +02:00
* goops.texi (VERSION): Bumped to version 0.3.
* goops-tutorial.texi, goops.texi: Updated to reflext new define-method syntax.
This commit is contained in:
parent
406d834426
commit
0442652715
3 changed files with 28 additions and 834 deletions
|
@ -1,3 +1,10 @@
|
|||
2001-03-09 Mikael Djurfeldt <mdj@linnaeus.mit.edu>
|
||||
|
||||
* goops.texi (VERSION): Bumped to version 0.3.
|
||||
|
||||
* goops-tutorial.texi, goops.texi: Updated to reflext new
|
||||
define-method syntax.
|
||||
|
||||
2001-03-09 Neil Jerram <neil@ossau.uklinux.net>
|
||||
|
||||
* Makefile.am: Change HTML to HTMLDOC, now that we're part of a
|
||||
|
|
|
@ -1,809 +0,0 @@
|
|||
@c Original attribution:
|
||||
|
||||
@c
|
||||
@c STk Reference manual (Appendix: An Introduction to STklos)
|
||||
@c
|
||||
@c Copyright © 1993-1999 Erick Gallesio - I3S-CNRS/ESSI <eg@unice.fr>
|
||||
@c Permission to use, copy, modify, distribute,and license this
|
||||
@c software and its documentation for any purpose is hereby granted,
|
||||
@c provided that existing copyright notices are retained in all
|
||||
@c copies and that this notice is included verbatim in any
|
||||
@c distributions. No written agreement, license, or royalty fee is
|
||||
@c required for any of the authorized uses.
|
||||
@c This software is provided ``AS IS'' without express or implied
|
||||
@c warranty.
|
||||
@c
|
||||
|
||||
@c Adapted for use in Guile with the authors permission
|
||||
|
||||
@c @macro goops @c was {\stklos}
|
||||
@c GOOPS
|
||||
@c @end macro
|
||||
|
||||
@c @macro guile @c was {\stk}
|
||||
@c Guile
|
||||
@c @end macro
|
||||
|
||||
This is chapter was originally written by Erick Gallesio as an appendix
|
||||
for the STk reference manual, and subsequently adapted to @goops{}.
|
||||
|
||||
@menu
|
||||
* Copyright::
|
||||
* Intro::
|
||||
* Class definition and instantiation::
|
||||
* Inheritance::
|
||||
* Generic functions::
|
||||
@end menu
|
||||
|
||||
@node Copyright, Intro, Tutorial, Tutorial
|
||||
@section Copyright
|
||||
|
||||
Original attribution:
|
||||
|
||||
STk Reference manual (Appendix: An Introduction to STklos)
|
||||
|
||||
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.
|
||||
|
||||
Adapted for use in Guile with the authors permission
|
||||
|
||||
@node Intro, Class definition and instantiation, Copyright, Tutorial
|
||||
@section Introduction
|
||||
|
||||
@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 the Gregor Kiczales @cite{Tiny-Clos}. It is very close
|
||||
to CLOS, the Common Lisp Object System (@cite{CLtL2}) but is adapted for
|
||||
the Scheme language.
|
||||
|
||||
Briefly stated, the @goops{} extension gives the user a full object
|
||||
oriented system with multiple inheritance and generic functions with
|
||||
multi-method dispatch. Furthermore, the implementation relies on a true
|
||||
meta object protocol, in the spirit of the one defined for CLOS
|
||||
(@cite{Gregor Kiczales: A Metaobject Protocol}).
|
||||
|
||||
The purpose of this tutorial is to introduce briefly the @goops{}
|
||||
package and in no case will it replace the @goops{} reference manual
|
||||
(which needs to be urgently written now@ @dots{}).
|
||||
|
||||
Note that the operations described in this tutorial resides in modules
|
||||
that may need to be imported before being available. The main module is
|
||||
imported by evaluating:
|
||||
|
||||
@lisp
|
||||
(use-modules (oop goops))
|
||||
@end lisp
|
||||
@findex (oop goops)
|
||||
@cindex main module
|
||||
@cindex loading
|
||||
@cindex preparing
|
||||
|
||||
@node Class definition and instantiation, Inheritance, Intro, Tutorial
|
||||
@section Class definition and instantiation
|
||||
|
||||
@menu
|
||||
* Class definition::
|
||||
@end menu
|
||||
|
||||
@node Class definition, , Class definition and instantiation, Class definition and instantiation
|
||||
@subsection Class definition
|
||||
|
||||
A new class is defined with the @code{define-class}@footnote{Don't
|
||||
forget to import the @code{(oop goops)} module} macro. The syntax of
|
||||
@code{define-class} is close to CLOS @code{defclass}:
|
||||
|
||||
@findex define-class
|
||||
@cindex class
|
||||
@lisp
|
||||
(define-class @var{class} (@var{superclass} @dots{})
|
||||
@var{slot-description} @dots{}
|
||||
@var{class-option} @dots{})
|
||||
@end lisp
|
||||
|
||||
Class options will not be discussed in this tutorial. The list of
|
||||
@var{superclass}es specifies which classes to inherit properties from
|
||||
@var{class} (see @ref{Inheritance} for more details). A
|
||||
@var{slot-description} gives the name of a slot and, eventually, some
|
||||
``properties'' of this slot (such as its initial value, the function
|
||||
which permit to access its value, @dots{}). Slot descriptions will be
|
||||
discussed in @ref{Slot description}.
|
||||
@cindex slot
|
||||
|
||||
As an example, let us define a type for representation of complex
|
||||
numbers in terms of real numbers. This can be done with the following
|
||||
class definition:
|
||||
|
||||
@lisp
|
||||
(define-class <complex> (<number>)
|
||||
r i)
|
||||
@end lisp
|
||||
|
||||
This binds the variable @code{<complex>}@footnote{@code{<complex>} is in
|
||||
fact a builtin class in GOOPS. Because of this, GOOPS will create a new
|
||||
class. The old class will still serve as the type for Guile's native
|
||||
complex numbers.} to a new class whose instances contain two
|
||||
slots. These slots are called @code{r} an @code{i} and we suppose here
|
||||
that they contain respectively the real part and the imaginary part of a
|
||||
complex number. Note that this class inherits from @code{<number>} which
|
||||
is a pre-defined class. (@code{<number>} is the direct super class of
|
||||
the pre-defined class @code{<complex>} which, in turn, is the super
|
||||
class of @code{<real>} which is the super of
|
||||
@code{<integer>}.)@footnote{With the new definition of @code{<complex>},
|
||||
a @code{<real>} is not a @code{<complex>} since @code{<real>} inherits
|
||||
from @code{ <number>} rather than @code{<complex>}. In practice,
|
||||
inheritance could be modified @emph{a posteriori}, if needed. However,
|
||||
this necessitates some knowledge of the meta object protocol and it will
|
||||
not be shown in this document}.
|
||||
|
||||
@node Inheritance, Generic functions, Class definition and instantiation, Tutorial
|
||||
@section Inheritance
|
||||
@c \label{inheritance}
|
||||
|
||||
@menu
|
||||
* Class hierarchy and inheritance of slots::
|
||||
* Instance creation and slot access::
|
||||
* Slot description::
|
||||
* Class precedence list::
|
||||
@end menu
|
||||
|
||||
@node Class hierarchy and inheritance of slots, Instance creation and slot access, Inheritance, Inheritance
|
||||
@subsection Class hierarchy and inheritance of slots
|
||||
Inheritance is specified upon class definition. As said in the
|
||||
introduction, @goops{} supports multiple inheritance. Here are some
|
||||
class definitions:
|
||||
|
||||
@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 super classes. In this
|
||||
case, the system will replace it by the 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 Figure@ 1.
|
||||
In this figure, the class @code{<top>} is also shown; this class is the
|
||||
super class of all Scheme objects. In particular, @code{<top>} is the
|
||||
super class of all standard Scheme types.
|
||||
|
||||
@example
|
||||
@group
|
||||
@image{hierarchy}
|
||||
@center @emph{Fig 1: A class hierarchy}
|
||||
@iftex
|
||||
@emph{(@code{<complex>} which is the direct subclass of @code{<number>}
|
||||
and the direct superclass of @code{<real>} has been omitted in this
|
||||
figure.)}
|
||||
@end iftex
|
||||
@end group
|
||||
@end example
|
||||
|
||||
The set of slots of a given class is calculated by taking the union of the
|
||||
slots of all its super class. For instance, each instance of the class
|
||||
D, defined before will have three slots (@code{a}, @code{b} and
|
||||
@code{d}). The slots of a class can be obtained by 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))
|
||||
@c used to be ((d) (a) (b) (c) (f))
|
||||
@end lisp
|
||||
|
||||
@emph{Note: } The order of slots is not significant.
|
||||
|
||||
@node Instance creation and slot access, Slot description, Class hierarchy and inheritance of slots, Inheritance
|
||||
@subsection Instance creation and slot access
|
||||
|
||||
Creation of an instance of a previously defined
|
||||
class can be done with the @code{make} procedure. This
|
||||
procedure takes one mandatory parameter which is the class of the
|
||||
instance which must be created and a list of optional
|
||||
arguments. Optional arguments are generally used to initialize some
|
||||
slots of the newly created instance. For instance, the following form
|
||||
|
||||
@findex make
|
||||
@cindex instance
|
||||
@lisp
|
||||
(define c (make <complex>))
|
||||
@end lisp
|
||||
|
||||
will create a new @code{<complex>} object and will bind it to the @code{c}
|
||||
Scheme variable.
|
||||
|
||||
Accessing the slots of the new complex number can be done with the
|
||||
@code{slot-ref} and the @code{slot-set!} primitives. @code{Slot-set!}
|
||||
primitive permits to set the value of an object slot and @code{slot-ref}
|
||||
permits to get its value.
|
||||
|
||||
@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
|
||||
|
||||
Using the @code{describe} function is a simple way to see all the
|
||||
slots of an object at one time: this function prints all the slots of an
|
||||
object on the standard output.
|
||||
|
||||
First load the module @code{(oop goops describe)}:
|
||||
|
||||
@example
|
||||
@code{(use-modules (oop goops describe))}
|
||||
@end example
|
||||
|
||||
The expression
|
||||
|
||||
@smalllisp
|
||||
(describe c)
|
||||
@end smalllisp
|
||||
|
||||
will now print the following information on the standard output:
|
||||
|
||||
@lisp
|
||||
#<<complex> 401d8638> is an instance of class <complex>
|
||||
Slots are:
|
||||
r = 10
|
||||
i = 3
|
||||
@end lisp
|
||||
|
||||
@node Slot description, Class precedence list, Instance creation and slot access, Inheritance
|
||||
@subsection Slot description
|
||||
@c \label{slot-description}
|
||||
|
||||
When specifying a slot, a set of options can be given to the
|
||||
system. Each option is specified with a keyword. The list of authorized
|
||||
keywords is given below:
|
||||
|
||||
@cindex keyword
|
||||
@itemize @bullet
|
||||
@item
|
||||
@code{#:init-value} permits to supply a default value for the slot. This
|
||||
default value is obtained by evaluating the form given after the
|
||||
@code{#:init-form} in the global environment, at class definition time.
|
||||
@cindex default slot value
|
||||
@findex #:init-value
|
||||
@cindex top level environment
|
||||
|
||||
@item
|
||||
@code{#:init-thunk} permits to supply a thunk that will provide a
|
||||
default value for the slot. The value is obtained by evaluating the
|
||||
thunk a instance creation time.
|
||||
@c CHECKME: in the global environment?
|
||||
@findex default slot value
|
||||
@findex #:init-thunk
|
||||
@cindex top level environment
|
||||
|
||||
@item
|
||||
@code{#:init-keyword} permits to specify the keyword for initializing a
|
||||
slot. The init-keyword may be provided during instance creation (i.e. in
|
||||
the @code{make} optional parameter list). Specifying such a keyword
|
||||
during instance initialization will supersede the default slot
|
||||
initialization possibly given with @code{#:init-form}.
|
||||
@findex #:init-keyword
|
||||
|
||||
@item
|
||||
@code{#:getter} permits to supply the name for the
|
||||
slot getter. The name binding is done in the
|
||||
environment of the @code{define-class} macro.
|
||||
@findex #:getter
|
||||
@cindex top level environment
|
||||
@cindex getter
|
||||
|
||||
@item
|
||||
@code{#:setter} permits to supply the name for the
|
||||
slot setter. The name binding is done in the
|
||||
environment of the @code{define-class} macro.
|
||||
@findex #:setter
|
||||
@cindex top level environment
|
||||
@cindex setter
|
||||
|
||||
@item
|
||||
@code{#:accessor} permits to supply the name for the
|
||||
slot accessor. The name binding is done in the global
|
||||
environment. An accessor permits to get and
|
||||
set the value of a slot. Setting the value of a slot is done with the extended
|
||||
version of @code{set!}.
|
||||
@findex set!
|
||||
@findex #:accessor
|
||||
@cindex top level environment
|
||||
@cindex accessor
|
||||
|
||||
@item
|
||||
@code{#:allocation} permits to specify how storage for
|
||||
the slot is allocated. Three kinds of allocation are provided.
|
||||
They are described below:
|
||||
|
||||
@itemize @minus
|
||||
@item
|
||||
@code{#:instance} indicates that each instance gets its own storage for
|
||||
the slot. This is the default.
|
||||
@item
|
||||
@code{#:class} indicates that there is one storage location used by all
|
||||
the direct and indirect instances of the class. This permits to define a
|
||||
kind of global variable which can be accessed only by (in)direct
|
||||
instances of the class which defines this slot.
|
||||
@item
|
||||
@code{#:each-subclass} indicates that there is one storage location used
|
||||
by all the direct instances of the class. In other words, if two classes
|
||||
are not siblings in the class hierarchy, they will not see the same
|
||||
value.
|
||||
@item
|
||||
@code{#:virtual} indicates that no storage will be allocated for this
|
||||
slot. It is up to the user to define a getter and a setter function for
|
||||
this slot. Those functions must be defined with the @code{#:slot-ref}
|
||||
and @code{#:slot-set!} options. See the example below.
|
||||
@findex #:slot-set!
|
||||
@findex #:slot-ref
|
||||
@findex #:virtual
|
||||
@findex #:class
|
||||
@findex #:each-subclass
|
||||
@findex #:instance
|
||||
@findex #:allocation
|
||||
@end itemize
|
||||
@end itemize
|
||||
|
||||
To illustrate slot description, we shall redefine the @code{<complex>} class
|
||||
seen before. A definition could be:
|
||||
|
||||
@lisp
|
||||
(define-class <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
|
||||
|
||||
With this definition, the @code{r} and @code{i} slot are set to 0 by
|
||||
default. Value of a slot can also be specified by calling @code{make}
|
||||
with the @code{#:r} and @code{#:i} keywords. Furthermore, the generic
|
||||
functions @code{get-r} and @code{set-r!} (resp. @code{get-i} and
|
||||
@code{set-i!}) are automatically defined by the system to read and write
|
||||
the @code{r} (resp. @code{i}) slot.
|
||||
|
||||
@lisp
|
||||
(define c1 (make <complex> #:r 1 #:i 2))
|
||||
(get-r c1) @result{} 1
|
||||
(set-r! c1 12)
|
||||
(get-r c1) @result{} 12
|
||||
(define c2 (make <complex> #:r 2))
|
||||
(get-r c2) @result{} 2
|
||||
(get-i c2) @result{} 0
|
||||
@end lisp
|
||||
|
||||
Accessors provide an uniform access for reading and writing an object
|
||||
slot. Writing a slot is done with an extended form of @code{set!}
|
||||
which is close to the Common Lisp @code{setf} macro. So, another
|
||||
definition of the previous @code{<complex>} class, using the
|
||||
@code{#:accessor} option, could be:
|
||||
|
||||
@findex set!
|
||||
@lisp
|
||||
(define-class <complex> (<number>)
|
||||
(r #:init-value 0 #:accessor real-part #:init-keyword #:r)
|
||||
(i #:init-value 0 #:accessor imag-part #:init-keyword #:i))
|
||||
@end lisp
|
||||
|
||||
Using this class definition, reading the real part of the @code{c}
|
||||
complex can be done with:
|
||||
@lisp
|
||||
(real-part c)
|
||||
@end lisp
|
||||
and setting it to the value contained in the @code{new-value} variable
|
||||
can be done using the extended form of @code{set!}.
|
||||
@lisp
|
||||
(set! (real-part c) new-value)
|
||||
@end lisp
|
||||
|
||||
Suppose now that we have to manipulate complex numbers with rectangular
|
||||
coordinates as well as with 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 uses virtual slots. A
|
||||
complete definition of the @code{<complex>} class using virtual slots is
|
||||
given in Figure@ 2.
|
||||
|
||||
@example
|
||||
@group
|
||||
@lisp
|
||||
(define-class <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
|
||||
@center @emph{Fig 2: A @code{<complex>} number class definition using virtual slots}
|
||||
@end group
|
||||
@end example
|
||||
|
||||
@sp 3
|
||||
This class definition implements two real slots (@code{r} and
|
||||
@code{i}). Values of the @code{m} and @code{a} virtual slots are
|
||||
calculated from real slot values. Reading a virtual slot leads to the
|
||||
application of the function defined in the @code{#:slot-ref}
|
||||
option. Writing such a slot leads to the application of the function
|
||||
defined in the @code{#:slot-set!} option. For instance, the following
|
||||
expression
|
||||
|
||||
@findex #:slot-set!
|
||||
@findex #:slot-ref
|
||||
@lisp
|
||||
(slot-set! c 'a 3)
|
||||
@end lisp
|
||||
|
||||
permits to set the angle of the @code{c} complex number. This expression
|
||||
conducts, in fact, to the evaluation of the following expression
|
||||
|
||||
@lisp
|
||||
((lambda o m)
|
||||
(let ((m (slot-ref o 'm)))
|
||||
(slot-set! o 'r (* m (cos a)))
|
||||
(slot-set! o 'i (* m (sin a))))
|
||||
c 3)
|
||||
@end lisp
|
||||
|
||||
A more complete example is given below:
|
||||
|
||||
@example
|
||||
@group
|
||||
@lisp
|
||||
(define c (make <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) @result{}
|
||||
#<<complex> 401e9b58> is an instance of class <complex>
|
||||
Slots are:
|
||||
r = 1
|
||||
i = 10
|
||||
m = 10.0498756211209
|
||||
a = 1.47112767430373
|
||||
@end lisp
|
||||
@end group
|
||||
@end example
|
||||
|
||||
Since initialization keywords have been defined for the four slots, we
|
||||
can now define the @code{make-rectangular} and @code{make-polar} standard
|
||||
Scheme primitives.
|
||||
|
||||
@lisp
|
||||
(define make-rectangular
|
||||
(lambda (x y) (make <complex> #:r x #:i y)))
|
||||
|
||||
(define make-polar
|
||||
(lambda (x y) (make <complex> #:magn x #:angle y)))
|
||||
@end lisp
|
||||
|
||||
@node Class precedence list, , Slot description, Inheritance
|
||||
@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 super classes 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 Figure@ 1, 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 Generic functions, , Inheritance, Tutorial
|
||||
@section Generic functions
|
||||
|
||||
@menu
|
||||
* Generic functions and methods::
|
||||
* Next-method::
|
||||
* Example::
|
||||
@end menu
|
||||
|
||||
@node Generic functions and methods, Next-method, Generic functions, Generic functions
|
||||
@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, Example, Generic functions and methods, Generic functions
|
||||
@subsection Next-method
|
||||
|
||||
When a generic function is called, the list of applicable methods is
|
||||
built. As mentioned before, the most specific method of this list is
|
||||
applied (see@ @ref{Generic functions and methods}). This method may call
|
||||
the next method in the list of applicable methods. This is done by using
|
||||
the special form @code{next-method}. Consider the following definitions
|
||||
|
||||
@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 those definitions,
|
||||
|
||||
@lisp
|
||||
(Test 1) @result{} (integer number top)
|
||||
(Test 1.0) @result{} (number top)
|
||||
(Test #t) @result{} (top)
|
||||
@end lisp
|
||||
|
||||
@node Example, , Next-method, Generic functions
|
||||
@subsection Example
|
||||
|
||||
In this section we shall continue to define operations on the @code{<complex>}
|
||||
class defined in Figure@ 2. 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 <complex>) (b <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 <complex>) (b <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
|
||||
Figure@ 3.
|
||||
|
||||
@example
|
||||
@group
|
||||
@lisp
|
||||
(define-generic new-+)
|
||||
|
||||
(let ((+ +))
|
||||
|
||||
(define-method new-+ ((a <real>) (b <real>)) (+ a b))
|
||||
|
||||
(define-method new-+ ((a <real>) (b <complex>))
|
||||
(make-rectangular (+ a (real-part b)) (imag-part b)))
|
||||
|
||||
(define-method new-+ ((a <complex>) (b <real>))
|
||||
(make-rectangular (+ (real-part a) b) (imag-part a)))
|
||||
|
||||
(define-method new-+ ((a <complex>) (b <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
|
||||
|
||||
@center @emph{Fig 3: Extending @code{+} for dealing with complex numbers}
|
||||
@end group
|
||||
@end example
|
||||
|
||||
@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 third parameter of
|
||||
a @code{define-method} is a parameter list which follow the conventions
|
||||
used for lambda expressions. 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 <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.
|
||||
|
|
@ -7,7 +7,7 @@
|
|||
@paragraphindent 0
|
||||
@c %**end of header
|
||||
|
||||
@set VERSION 0.2
|
||||
@set VERSION 0.3
|
||||
|
||||
@dircategory The Algorithmic Language Scheme
|
||||
@direntry
|
||||
|
@ -25,7 +25,7 @@ Guile
|
|||
@ifinfo
|
||||
This file documents GOOPS, an object oriented extension for Guile.
|
||||
|
||||
Copyright (C) 1999, 2000 Free Software Foundation
|
||||
Copyright (C) 1999, 2000, 2001 Free Software Foundation
|
||||
|
||||
Permission is granted to make and distribute verbatim copies of
|
||||
this manual provided the copyright notice and this permission notice
|
||||
|
@ -157,7 +157,7 @@ We're now ready to try some basic GOOPS functionality.
|
|||
|
||||
@smalllisp
|
||||
@group
|
||||
(define-method + ((x <string>) (y <string>))
|
||||
(define-method (+ (x <string>) (y <string>))
|
||||
(string-append x y))
|
||||
|
||||
(+ 1 2) --> 3
|
||||
|
@ -176,7 +176,7 @@ We're now ready to try some basic GOOPS functionality.
|
|||
@group
|
||||
(use-modules (ice-9 format))
|
||||
|
||||
(define-method write ((obj <2D-vector>) port)
|
||||
(define-method (write (obj <2D-vector>) port)
|
||||
(display (format #f "<~S, ~S>" (x-component obj) (y-component obj))
|
||||
port))
|
||||
|
||||
|
@ -186,7 +186,7 @@ v --> <3, 4>
|
|||
@end group
|
||||
|
||||
@group
|
||||
(define-method + ((x <2D-vector>) (y <2D-vector>))
|
||||
(define-method (+ (x <2D-vector>) (y <2D-vector>))
|
||||
(make <2D-vector>
|
||||
#:x (+ (x-component x) (x-component y))
|
||||
#:y (+ (y-component x) (y-component y))))
|
||||
|
@ -557,7 +557,7 @@ Reference methods for an accessor are defined in the same way as generic
|
|||
function methods.
|
||||
|
||||
@example
|
||||
(define-method perimeter ((s <square>))
|
||||
(define-method (perimeter (s <square>))
|
||||
(* 4 (side-length s)))
|
||||
@end example
|
||||
|
||||
|
@ -566,7 +566,7 @@ Setter methods for an accessor are defined by specifying ``(setter
|
|||
call.
|
||||
|
||||
@example
|
||||
(define-method (setter perimeter) ((s <square>) (n <number>))
|
||||
(define-method ((setter perimeter) (s <square>) (n <number>))
|
||||
(set! (side-length s) (/ n 4)))
|
||||
@end example
|
||||
|
||||
|
@ -864,7 +864,7 @@ specialized method of the generic function @code{initialize}, whose
|
|||
signature is
|
||||
|
||||
@example
|
||||
(define-method initialize ((object <object>) initargs) ...)
|
||||
(define-method (initialize (object <object>) initargs) ...)
|
||||
@end example
|
||||
|
||||
The initialization of instances of any given class can be customized by
|
||||
|
@ -1086,7 +1086,7 @@ allocation to do this.
|
|||
|
||||
(let ((batch-allocation-count 0)
|
||||
(batch-get-n-set #f))
|
||||
(define-method compute-get-n-set ((class <batched-allocation-metaclass>) s)
|
||||
(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,
|
||||
|
@ -1665,21 +1665,17 @@ procedures described in this section may disappear as well.
|
|||
|
||||
To add a method to a generic function, use the @code{define-method} form.
|
||||
|
||||
@deffn syntax define-method symbol (parameter @dots{}) . body
|
||||
Define a method for the generic function or accessor @var{symbol} with
|
||||
@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{symbol} must be either a symbol for a variable bound to a generic
|
||||
function or accessor, or @code{(setter @var{accessor-symbol})}, where
|
||||
@var{accessor-symbol} is a symbol for a variable bound to an accessor.
|
||||
If the former, @code{define-method} defines a reference method for the
|
||||
specified generic function or accessor; if the latter,
|
||||
@code{define-method} defines a setter method for the specified accessor.
|
||||
The @var{symbol} parameter is subject to these restrictions (rather than
|
||||
being allowed to be anything that evaluates to a generic function) so
|
||||
that @code{define-method} can construct a call to @code{define-generic}
|
||||
or @code{define-accessor} if @var{symbol} is not already defined as a
|
||||
generic function.
|
||||
@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
|
||||
|
@ -1695,7 +1691,7 @@ can be applied.
|
|||
procedure definitions of the form
|
||||
|
||||
@example
|
||||
(define name (lambda (formals @dots{}) . body))
|
||||
(define (name formals @dots{}) . body)
|
||||
@end example
|
||||
|
||||
The most important difference is that each formal parameter, apart from the
|
||||
|
@ -1997,7 +1993,7 @@ is specialized for this metaclass:
|
|||
@example
|
||||
(define-class <can-be-nameless> (<class>))
|
||||
|
||||
(define-method class-redefinition ((old <can-be-nameless>) (new <class>))
|
||||
(define-method (class-redefinition (old <can-be-nameless>) (new <class>))
|
||||
new)
|
||||
@end example
|
||||
|
||||
|
@ -2304,7 +2300,7 @@ Return an expression that prints to show the definition of method
|
|||
@example
|
||||
(define-generic cube)
|
||||
|
||||
(define-method cube ((n <number>))
|
||||
(define-method (cube (n <number>))
|
||||
(* n n n))
|
||||
|
||||
(map method-source (generic-function-methods cube))
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue