1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
|
@node Generic dispatch system
@section Generic dispatch system
@cindex multimethod dispatch
@cindex type dispatch
@cindex generic functions
@cindex generic predicate dispatch
@stindex methods
@stindex meta-methods
Scheme48 supports a CLOS-style generic procedure dispatch system, based
on type predicates. The main interface is exported by @code{methods}.
The internals of the system are exposed by the @code{meta-methods}
structure, but they are not documented here. The generic dispatch
system is used in Scheme48's @embedref{Writer, writer} and numeric
system.
@c @embedref{numeric system, Numeric system}.
@dfn{Types} in Scheme48's generic dispatch system are represented using
type predicates, rather than having every object have a single,
well-defined `class.' The naming convention for simple types is to
prefix the type name with a colon. The types support multiple
inheritance. Method specificity is determined based on descending
order of argument importance. That is, given two methods, @var{M} &
@var{N}, such that they are both applicable to a given sequence of
arguments, and an index @var{i} into that sequence, such that @var{i}
is the first index in @var{M}'s & @var{N}'s lists of argument type
specifiers, from left to right, where the type differs: if the type for
@var{M}'s argument at @var{i} is more specific than the corresponding
type in @var{N}'s specifiers, @var{M} is considered to be more specific
than @var{N}, even if the remaining argument type specifiers in @var{N}
are more specific.
@deffn syntax define-simple-type name (supertype @dots{}) predicate
Defines @var{name} to be a @dfn{simple type} with the given predicate
and the given supertypes.
@end deffn
@deffn procedure singleton value @returns{} simple-type
Creates a @dfn{singleton type} that matches only @var{value}.
@end deffn
@deffn syntax define-generic proc-name method-table-name [prototype]
Defines @var{proc-name} to be a @dfn{generic procedure} that, when
invoked, will dispatch on its arguments via the @dfn{method table} that
@var{method-table-name} is defined to be and apply the most specific
method it can determine defined in the @var{method-table-name} method
table to its arguments. The convention for naming variables that will
be bound to method tables is to add an ampersand to the front of the
name. @var{Prototype} is a suggestion for what method prototypes
should follow the shape of, but it is currently ignored.
@end deffn
@deffn syntax define-method method-table prototype body
Adds a @dfn{method} to @var{method-table}, which is usually one defined
by @code{define-generic}.@footnote{There is an internal interface, a
sort of meta-object protocol, to the method dispatch system, but it is
not yet documented.} @var{Prototype} should be a list whose elements
may be either identifiers, in which case that parameter is not used for
dispatching, or lists of two elements, the @code{car} of which is the
parameter name and the @code{cadr} of which should evaluate to the type
on which to dispatch. As in many generic dispatch systems of similar
designs, methods may invoke the next-most-specific method. By default,
the name @code{next-method} is bound in @var{body} to a nullary
procedure that calls the next-most-specific method. The name of this
procedure may be specified by the user by putting the sequence
@code{"next" @var{next-method-name}} in @var{prototype}, in which case
it will be @var{next-method-name} that is bound to that procedure. For
example:
@lisp
(define-method &frob ((foo :bar) "next" frobozz)
(if (mumble? foo)
(frobozz) ; Invoke the next method.
(yargh blargle foo)))@end lisp
@end deffn
A number of simple types are already defined & exported by the
@code{methods} structure. Entries are listed as @code{@var{type-name}
<- (@var{supertype} @dots{}), @var{predicate}}
@itemize
@item @code{:values <- (), (lambda (x) #t)} --- Abstract supertype of
all run-time values
@item @code{:value <- (:values), (lambda (x) #t)} --- Abstract
supertype of all first-class values
@item @code{:zero <- (:values), (lambda (x) #f)} --- Type that no
objects satisfy
@item @code{:number <- (:value), number?}
@item @code{:complex <- (:number), complex?} --- (This happens to be
equivalent to @code{:number}.)
@item @code{:real <- (:complex), real?}
@item @code{:rational <- (:real), rational?}
@item @code{:integer <- (:rational), integer?}
@item @code{:exact-integer <- (:integer),
(lambda (x) (and (integer? x) (exact? x)))}
@item @code{:boolean <- (:value), boolean?}
@item @code{:symbol <- (:value), symbol?}
@item @code{:char <- (:value), char?}
@item @code{:null <- (:value), null?}
@item @code{:pair <- (:value), pair?}
@item @code{:vector <- (:value), vector?}
@item @code{:string <- (:value), string?}
@item @code{:procedure <- (:value), procedure?}
@item @code{:input-port <- (:value), input-port?}
@item @code{:output-port <- (:value), output-port?}
@item @code{:eof-object <- (:value), eof-object?}
@item @code{:record <- (:value), record?}
@end itemize
|