trait.ss: Object-Oriented Traits
To load: (require (lib "trait.ss"))
A trait is a collection of methods that can be converted to a mixin and then applied to a class. Before a trait is converted to a mixin, the methods of a trait can be individually renamed, and multiple traits can be merged to form a new trait. The trait constructs provided by the trait.ss library work with the classes of the class.ss library (see section 6).
The trait
form creates a new trait:
(trait trait-clause ···) trait-clause is one of (public optionally-renamed-id ···) (pubment optionally-renamed-id ···) (public-final optionally-renamed-id ···) (override optionally-renamed-id ···) (overment optionally-renamed-id ···) (override-final optionally-renamed-id ···) (augment optionally-renamed-id ···) (augride optionally-renamed-id ···) (augment-final optionally-renamed-id ···) (inherit optionally-renamed-id ···) (inherit/super optionally-renamed-id ···) (inherit/inner optionally-renamed-id ···) method-definition (field field-declaration ···) (inherit-field optionally-renamed-id ···)
The body of a trait
form is similar to the body of a
class
form, but restricted to non-private method definitions.
In particular, the grammar of optionally-renamed-id
,
method-definition
, and field-declaration
are the same as
for class
(see
section 6), and every method-definition
must have a
corresponding declaration (one of public
, override
,
etc.). As in class
, uses of method names in direct calls,
super
calls, and inner
calls depend on bringing
method names into scope via inherit
, inherit/super
,
inherit/inner
, and other method declarations in the same
trait; an exception, compared to class
is that
overment
binds a method name only in the corresponding
method, and not in other methods of the same trait. Finally, macros
such as public*
and define/public
work in trait
as in class
.
(trait->mixin
trait
)
converts a trait to a mixin, which can be
applied to a class to produce a new class. An expression of the form
(trait->mixin (trait trait-clause ···))
is equivalent to
(lambda (%) (class % trait-clause ··· (super-new)))
Normally, however, a trait's methods are changed and combined with other traits before converting to a mixin.
(trait-sum
trait
···1)
produces a trait that combines all of the
methods of the given traits. For example,
(define t1
(trait
(define/public (m1) 1)))
(define t2
(trait
(define/public (m2) 2)))
(define t3 (trait-sum
t1 t2))
creates a trait t3
that is equivalent to
(trait (define/public (m1) 1) (define/public (m2) 2))
but t1
and t2
can still be used individually or
combined with other traits.
When traits are combined with trait-sum
, the combination
drops inherit
, inherit/super
, inherit/inner
, and inherit-field
declarations when a definition is supplied for the same method or field name by
another trait. The trait-sum
operation fails (the
exn:fail:contract
exception is raised) if any of the traits to combine define a
method or field with the same name, or if an inherit/super
or
inherit/inner
declaration to be dropped is inconsistent with the
supplied definition. In other words, declaring a method with
inherit
, inherit/super
, or inherit/inner
,
does not count as defining the method; at the same time, for example,
a trait that contains an inherit/super
declaration for a
method m
cannot be combinaed with a trait that defines m
as augment
, since no class could satisfy the requirements of
both augment
and inherit/super
when the trait is later
converted to a mixin and applied to a class.
(trait-exclude trait-expr identifier)
produces a new trait
that is like the result of trait-expr
, but with the definition
of a method named by identifier
removed; as the method
definition is removed, either a inherit
,
inherit/super
, or inherit/inner
declaration is
added:
A method declared with
public
,pubment
, orpublic-final
is replaced with ainherit
declaration.A method declared with
override
oroverride-final
is replaced with ainherit/super
declaration.A method declared with
augment
,augride
, oraugment-final
is replaced with ainherit/inner
declaration.A method declared with
overment
is not replaced with anyinherit
declaration.
If the trait produced by trait-expr
has no method definition for
identifier
, the exn:fail:contract
exception is raised.
(trait-exclude-field trait-expr identifier)
produces a new trait
that is like the result of trait-expr
, but with the definition
of a field named by identifier
removed; as the field
definition is removed, an inherit-field
declaration is
added.
(trait-alias trait-expr identifier new-identifier)
produces a
new trait that is like the result of trait-expr
, but the
definition and declaration of the method named by identifier
is
duplicated with the name new-identifier
. The consistency
requirements for the resulting trait are the same as for
trait-sum
, otherwise the exn:fail:contract
exception is raised. This
operation does not rename any other use of identifier
, such as
in method calls (even method calls to identifer
in the cloned
definition for new-identifier
).
(trait-rename trait-expr identifier new-identifier)
produces
a new trait that is like the result of trait-expr
, but all
definitions and references to methods named identifier
are
replaced by definitions and references to methods named by
new-identifier
. The consistency requirements for the resulting
trait is the same as for trait-sum
, otherwise the
exn:fail:contract
exception is raised.
(trait-rename-field trait-expr identifier new-identifier)
produces
a new trait that is like the result of trait-expr
, but all
definitions and references to fields named identifier
are
replaced by definitions and references to fields named by
new-identifier
. The consistency requirements for the resulting
trait is the same as for trait-sum
, otherwise the
exn:fail:contract
exception is raised.
External identifiers in trait
, trait-exclude
, trait-exclude-field
,
trait-alias
, trait-rename
, and trait-rename-field
forms are subject to
binding via define-member-name
and
define-local-member-name
(see section 6.3.3.3). Although
private
methods or fields are not allowed in a trait
form,
they can be simulated by using a public
or field
declaration and a
name whose scope is limited to the trait
form.