mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-05-20 19:50:24 +02:00
Edit `Generic Functions and Accessors'
* doc/ref/goops.texi (Generic Function Internals): Moved to MOP section at end of chapter. (Basic Generic Function Creation): Flattened into parent, and text simplified. (Extending Primitives): Renamed from `Extending Guiles Primitives'; removed `generic-capability?', which is no longer available; simplified a bit. (Merging Generics): New subsection for the material about merging; text simplified a bit.
This commit is contained in:
parent
03604fcf50
commit
eed4cc7bac
1 changed files with 150 additions and 198 deletions
|
@ -768,61 +768,67 @@ will handle a call like
|
||||||
by calling the most specialized method of @code{accessor} that matches
|
by calling the most specialized method of @code{accessor} that matches
|
||||||
the classes of @code{args} and @code{value}.
|
the classes of @code{args} and @code{value}.
|
||||||
|
|
||||||
@menu
|
The following forms define a variable as a generic function or accessor.
|
||||||
* Basic Generic Function Creation::
|
Depending on that variable's previous value, the generic function may be
|
||||||
* Generic Function Internals::
|
created empty --- with no methods --- or with methods that are inferred
|
||||||
* Extending Guiles Primitives::
|
from the previous value.
|
||||||
@end menu
|
|
||||||
|
|
||||||
@node Basic Generic Function Creation
|
|
||||||
@subsection Basic Generic Function Creation
|
|
||||||
|
|
||||||
The following forms may be used to bind a variable to a generic
|
|
||||||
function. Depending on that variable's pre-existing value, the generic
|
|
||||||
function may be created empty - with no methods - or it may contain
|
|
||||||
methods that are inferred from the pre-existing value.
|
|
||||||
|
|
||||||
It is not, in general, necessary to use @code{define-generic} or
|
|
||||||
@code{define-accessor} before defining methods for the generic function
|
|
||||||
using @code{define-method}, since @code{define-method} will
|
|
||||||
automatically interpolate a @code{define-generic} call, or upgrade an
|
|
||||||
existing generic to an accessor, if that is implied by the
|
|
||||||
@code{define-method} call. Note in particular that,
|
|
||||||
if the specified variable already has a @emph{generic function} value,
|
|
||||||
@code{define-generic} and @code{define-accessor} will @emph{discard} it!
|
|
||||||
Obviously it is application-dependent whether this is desirable or not.
|
|
||||||
|
|
||||||
If, for example, you wanted to extend @code{+} for a class representing
|
|
||||||
a new numerical type, you probably want to inherit any existing methods
|
|
||||||
for @code{+} and so should not use @code{define-generic}. If, on the
|
|
||||||
other hand, you do not want to risk inheriting methods whose behaviour
|
|
||||||
might surprise you, you can use @code{define-generic} or
|
|
||||||
@code{define-accessor} to wipe the slate clean.
|
|
||||||
|
|
||||||
@deffn syntax define-generic symbol
|
@deffn syntax define-generic symbol
|
||||||
Create a generic function with name @var{symbol} and bind it to the
|
Create a generic function with name @var{symbol} and bind it to the
|
||||||
variable @var{symbol}.
|
variable @var{symbol}. If @var{symbol} was previously bound to a Scheme
|
||||||
|
procedure (or procedure-with-setter), the old procedure (and setter) is
|
||||||
If the variable @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
|
incorporated into the new generic function as its default procedure (and
|
||||||
setter). Any other previous value that was bound to @var{symbol},
|
setter). Any other previous value, including an existing generic
|
||||||
including an existing generic function, is overwritten by the new
|
function, is discarded and replaced by a new, empty generic function.
|
||||||
generic function.
|
|
||||||
@end deffn
|
@end deffn
|
||||||
|
|
||||||
@deffn syntax define-accessor symbol
|
@deffn syntax define-accessor symbol
|
||||||
Create an accessor with name @var{symbol} and bind it to the variable
|
Create an accessor with name @var{symbol} and bind it to the variable
|
||||||
@var{symbol}.
|
@var{symbol}. If @var{symbol} was previously bound to a Scheme
|
||||||
|
procedure (or procedure-with-setter), the old procedure (and setter) is
|
||||||
If the variable @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
|
incorporated into the new accessor as its default procedure (and
|
||||||
setter). Any other previous value that was bound to @var{symbol},
|
setter). Any other previous value, including an existing generic
|
||||||
including an existing generic function or accessor, is overwritten by
|
function or accessor, is discarded and replaced by a new, empty
|
||||||
the new definition.
|
accessor.
|
||||||
@end deffn
|
@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.
|
GOOPS generic functions and accessors often have short, generic names.
|
||||||
For example, if a vector package provides an accessor for the X
|
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
|
coordinate of a vector, that accessor may just be called @code{x}. It
|
||||||
|
@ -835,11 +841,13 @@ That raises the question, however, of what happens when different
|
||||||
packages define a generic function with the same name. Suppose we
|
packages define a generic function with the same name. Suppose we
|
||||||
work with a graphical package which needs to use two independent vector
|
work with a graphical package which needs to use two independent vector
|
||||||
packages for 2D and 3D vectors respectively. If both packages export
|
packages for 2D and 3D vectors respectively. If both packages export
|
||||||
@code{x} we will encounter a name collision.
|
@code{x}, what does the code using those packages end up with?
|
||||||
|
|
||||||
This can be resolved automagically with the duplicates handler
|
@ref{Creating Guile Modules,,duplicate binding handlers} explains how
|
||||||
@code{merge-generics} which gives the module system license to merge
|
this is resolved for conflicting bindings in general. For generics,
|
||||||
all generic functions sharing a common name:
|
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
|
@lisp
|
||||||
(define-module (math 2D-vectors)
|
(define-module (math 2D-vectors)
|
||||||
|
@ -856,155 +864,30 @@ all generic functions sharing a common name:
|
||||||
#:duplicates merge-generics)
|
#:duplicates merge-generics)
|
||||||
@end lisp
|
@end lisp
|
||||||
|
|
||||||
The generic function @code{x} in @code{(my-module)} will now share
|
The generic function @code{x} in @code{(my-module)} will now incorporate
|
||||||
methods with @code{x} in both imported modules.
|
all of the methods of @code{x} from both imported modules.
|
||||||
|
|
||||||
There will, in fact, now be three distinct generic functions named
|
To be precise, there will now be three distinct generic functions named
|
||||||
@code{x}: @code{x} in @code{(2D-vectors)}, @code{x} in
|
@code{x}: @code{x} in @code{(math 2D-vectors)}, @code{x} in @code{(math
|
||||||
@code{(3D-vectors)}, and @code{x} in @code{(my-module)}. The last
|
3D-vectors)}, and @code{x} in @code{(my-module)}; and these functions
|
||||||
function will be an @code{<extended-generic>}, extending the previous
|
share their methods in an interesting and dynamic way.
|
||||||
two functions.
|
|
||||||
|
|
||||||
Let's call the imported generic functions the "ancestor functions".
|
To explain, let's call the imported generic functions (in @code{(math
|
||||||
The generic function @code{x} in @code{(my-module)} is, in turn, a
|
2D-vectors)} and @code{(math 3D-vectors)}) the @dfn{ancestors}, and the
|
||||||
"descendant function" of the imported functions, extending its
|
merged generic function (in @code{(my-module)}), the @dfn{descendant}.
|
||||||
ancestors.
|
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.
|
||||||
|
|
||||||
For any generic function G, the applicable methods are selected from
|
Thus ancestor functions effectively share methods with their
|
||||||
the union of the methods of the descendant functions, the methods of G
|
descendants, and vice versa. In the example above, @code{x} in
|
||||||
itself and the methods of the ancestor functions.
|
@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
|
||||||
This, ancestor functions share methods with their descendants and vice
|
@code{(math 2D-vectors)} doesn't share methods with @code{x} in
|
||||||
versa. This implies that @code{x} in @code{(math 2D-vectors)} will
|
@code{(math 3D-vectors)}, so modularity is still preserved.} Sharing is
|
||||||
share the methods of @code{x} in @code{(my-module)} and vice versa,
|
dynamic, so adding another new method to a descendant implies adding it
|
||||||
while @code{x} in @code{(math 2D-vectors)} doesn't share the methods
|
to that descendant's ancestors too.
|
||||||
of @code{x} in @code{(math 3D-vectors)}, thus preserving modularity.
|
|
||||||
|
|
||||||
Sharing is dynamic, so that adding new methods to a descendant implies
|
|
||||||
adding it to the ancestor.
|
|
||||||
|
|
||||||
If duplicates checking is desired in the above example, the following
|
|
||||||
form of the @code{#:duplicates} option can be used instead:
|
|
||||||
|
|
||||||
@lisp
|
|
||||||
#:duplicates (merge-generics check)
|
|
||||||
@end lisp
|
|
||||||
|
|
||||||
@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 Extending Guiles Primitives
|
|
||||||
@subsection Extending Guile's Primitives
|
|
||||||
|
|
||||||
When GOOPS is loaded, 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. For
|
|
||||||
primitives that are extended in this way, the result from the user-
|
|
||||||
or application-level point of view is that the extended primitive
|
|
||||||
behaves exactly like a generic function with the C-coded implementation
|
|
||||||
as its default method.
|
|
||||||
|
|
||||||
The @code{generic-capability?} predicate should be used to determine
|
|
||||||
whether a particular primitive is extensible in this way.
|
|
||||||
|
|
||||||
@deffn {primitive procedure} generic-capability? primitive
|
|
||||||
Return @code{#t} if @var{primitive} can be extended by giving it a
|
|
||||||
generic function definition, otherwise @code{#f}.
|
|
||||||
@end deffn
|
|
||||||
|
|
||||||
Even when a primitive procedure is extensible like this, its generic
|
|
||||||
function definition is not created until it is needed by a call to
|
|
||||||
@code{define-method}, or until the application explicitly requests it
|
|
||||||
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, or if its generic capability
|
|
||||||
has not yet been enabled, whether implicitly (by @code{define-method})
|
|
||||||
or explicitly (by @code{enable-primitive-generic!}).
|
|
||||||
@end deffn
|
|
||||||
|
|
||||||
Note that the distinction between, on the one hand, primitives with
|
|
||||||
additional generic function definitions and, on the other hand, generic
|
|
||||||
functions with a default method, may disappear when GOOPS is fully
|
|
||||||
integrated into the core of Guile. Consequently, the
|
|
||||||
procedures described in this section may disappear as well.
|
|
||||||
|
|
||||||
@node Adding Methods to Generic Functions
|
@node Adding Methods to Generic Functions
|
||||||
@section Adding Methods to Generic Functions
|
@section Adding Methods to Generic Functions
|
||||||
|
@ -1144,9 +1027,8 @@ Add method @var{method} to the generic function @var{generic}.
|
||||||
|
|
||||||
@deffn method add-method! (proc <procedure>) (method <method>)
|
@deffn method add-method! (proc <procedure>) (method <method>)
|
||||||
If @var{proc} is a procedure with generic capability (@pxref{Extending
|
If @var{proc} is a procedure with generic capability (@pxref{Extending
|
||||||
Guiles Primitives,, generic-capability?}), upgrade it to a
|
Primitives,, generic-capability?}), upgrade it to a primitive generic
|
||||||
primitive generic and add @var{method} to its generic function
|
and add @var{method} to its generic function definition.
|
||||||
definition.
|
|
||||||
@end deffn
|
@end deffn
|
||||||
|
|
||||||
@deffn method add-method! (pg <primitive-generic>) (method <method>)
|
@deffn method add-method! (pg <primitive-generic>) (method <method>)
|
||||||
|
@ -1807,6 +1689,7 @@ GOOPS' power, by customizing the behaviour of GOOPS itself.
|
||||||
* Customizing Instance Creation::
|
* Customizing Instance Creation::
|
||||||
* Class Redefinition::
|
* Class Redefinition::
|
||||||
* Method Definition::
|
* Method Definition::
|
||||||
|
* Generic Function Internals::
|
||||||
* Generic Function Invocation::
|
* Generic Function Invocation::
|
||||||
@end menu
|
@end menu
|
||||||
|
|
||||||
|
@ -2685,13 +2568,82 @@ a generic function (the most common case)
|
||||||
a procedure
|
a procedure
|
||||||
|
|
||||||
@item
|
@item
|
||||||
a primitive generic (@pxref{Extending Guiles Primitives})
|
a primitive generic (@pxref{Extending Primitives})
|
||||||
@end itemize
|
@end itemize
|
||||||
|
|
||||||
By defining further methods for @code{add-method!}, you can
|
By defining further methods for @code{add-method!}, you can
|
||||||
theoretically handle adding methods to further types of target.
|
theoretically handle adding methods to further types of target.
|
||||||
@end itemize
|
@end itemize
|
||||||
|
|
||||||
|
@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
|
@node Generic Function Invocation
|
||||||
@subsection Generic Function Invocation
|
@subsection Generic Function Invocation
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue