1
Fork 0
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:
Neil Jerram 2010-10-12 23:40:01 +01:00
parent 03604fcf50
commit eed4cc7bac

View file

@ -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