Okay, finalized initial version of this, which I'll probably go ahead and
merge to trunk. svn: r18392
This commit is contained in:
parent
75dd3eeb2b
commit
dd96465208
|
@ -810,12 +810,18 @@ Using this form in conjunction with trait operators such as
|
||||||
|
|
||||||
As classes are values, they can flow across contract boundaries, and we
|
As classes are values, they can flow across contract boundaries, and we
|
||||||
may wish to protect parts of a given class with contracts. For this,
|
may wish to protect parts of a given class with contracts. For this,
|
||||||
the @scheme[class/c] form is used. In its simplest form, @scheme[class/c]
|
the @scheme[class/c] form is used. The @scheme[class/c] form has many
|
||||||
protects the public fields and methods of objects instantiated from the
|
subforms, which describe two types of contracts on fields and methods:
|
||||||
contracted class. There is also an @scheme[object/c] form that can be used
|
those that affect uses via instantiated objects and those that affect
|
||||||
to similarly protect the public fields and methods of a particular object.
|
subclasses.
|
||||||
Take the following definition of @scheme[animal%], which uses a public field
|
|
||||||
for its @scheme[size] attribute:
|
@subsection{External Class Contracts}
|
||||||
|
|
||||||
|
In its simplest form, @scheme[class/c] protects the public fields and methods
|
||||||
|
of objects instantiated from the contracted class. There is also an
|
||||||
|
@scheme[object/c] form that can be used to similarly protect the public fields
|
||||||
|
and methods of a particular object. Take the following definition of
|
||||||
|
@scheme[animal%], which uses a public field for its @scheme[size] attribute:
|
||||||
|
|
||||||
@schemeblock[
|
@schemeblock[
|
||||||
(define animal%
|
(define animal%
|
||||||
|
@ -878,16 +884,15 @@ on both @scheme[size] and @scheme[eat] are enforced:
|
||||||
(define giant (new (class object% (super-new) (field [size 'large]))))
|
(define giant (new (class object% (super-new) (field [size 'large]))))
|
||||||
(send bob eat giant)]
|
(send bob eat giant)]
|
||||||
|
|
||||||
There are two important caveats for these contracts, which we will
|
There are two important caveats for external class contracts. First,
|
||||||
call @deftech{external class contracts}. First, external method contracts
|
external method contracts are only enforced when the target of dynamic
|
||||||
are only enforced when the target of dynamic dispatch is the method
|
dispatch is the method implementation of the contracted class, which
|
||||||
implementation of the contracted class, which lies within the contract
|
lies within the contract boundary. Overriding that implementation, and
|
||||||
boundary. Overriding that implementation, and thus changing the target
|
thus changing the target of dynamic dispatch, will mean that the contract
|
||||||
of dynamic dispatch, will mean that the contract is no longer enforced
|
is no longer enforced for clients, since accessing the method no longer
|
||||||
for clients, since accessing the method no longer crosses the contract
|
crosses the contract boundary. Unlike external method contracts, external
|
||||||
boundary. Unlike external method contracts, external field contracts
|
field contracts are always enforced for clients of subclasses, since fields
|
||||||
are always enforced for clients of subclasses, since fields cannot be
|
cannot be overridden or shadowed.
|
||||||
overridden or shadowed.
|
|
||||||
|
|
||||||
Second, these contracts do not restrict subclasses of @scheme[animal%]
|
Second, these contracts do not restrict subclasses of @scheme[animal%]
|
||||||
in any way. Fields and methods that are inherited and used by subclasses
|
in any way. Fields and methods that are inherited and used by subclasses
|
||||||
|
@ -908,6 +913,8 @@ both caveats:
|
||||||
(send elephant eat (new object%))
|
(send elephant eat (new object%))
|
||||||
(get-field size elephant)]
|
(get-field size elephant)]
|
||||||
|
|
||||||
|
@subsection{Internal Class Contracts}
|
||||||
|
|
||||||
Notice that retrieving the @scheme[size] field from the object
|
Notice that retrieving the @scheme[size] field from the object
|
||||||
@scheme[elephant] blames @scheme[animal%] for the contract violation.
|
@scheme[elephant] blames @scheme[animal%] for the contract violation.
|
||||||
This blame is correct, but unfair to the @scheme[animal%] class,
|
This blame is correct, but unfair to the @scheme[animal%] class,
|
||||||
|
@ -935,43 +942,52 @@ This class contract not only ensures that objects of class @scheme[animal%]
|
||||||
are protected as before, but also ensure that subclasses of @scheme[animal%]
|
are protected as before, but also ensure that subclasses of @scheme[animal%]
|
||||||
only store appropriate values within the @scheme[size] field and use
|
only store appropriate values within the @scheme[size] field and use
|
||||||
the implementation of @scheme[size] from @scheme[animal%] appropriately.
|
the implementation of @scheme[size] from @scheme[animal%] appropriately.
|
||||||
These contract forms only affect uses within the class hierarchy, so the
|
These contract forms only affect uses within the class hierarchy, and only
|
||||||
@scheme[override] form does not automatically enter subclasses into
|
for method calls that cross the contract boundary.
|
||||||
obligations when objects of those classes are used. Instead, such contracts
|
|
||||||
are only checked when uses of the method within the superclass dynamically
|
That means that @scheme[inherit] will only affect subclass uses of a method
|
||||||
dispatch to the subclass's implementation. The following example shows
|
until a subclass overrides that method, and that @scheme[override] only
|
||||||
this difference:
|
affects calls from the superclass into a subclass's overriding implementation
|
||||||
|
of that method. Since these only affect internal uses, the @scheme[override]
|
||||||
|
form does not automatically enter subclasses into obligations when objects of
|
||||||
|
those classes are used. Also, use of @scheme[override] only makes sense, and
|
||||||
|
thus can only be used, for methods where no Beta-style augmentation has taken
|
||||||
|
place. The following example shows this difference:
|
||||||
|
|
||||||
@schemeblock[
|
@schemeblock[
|
||||||
(define/contract glutton%
|
(define/contract sloppy-eater%
|
||||||
(class/c (override [eat (->m edible/c void?)]))
|
(class/c [eat (->m edible/c edible/c)])
|
||||||
(class animal%
|
(begin
|
||||||
(super-new)
|
(define/contract glutton%
|
||||||
(inherit eat)
|
(class/c (override [eat (->m edible/c void?)]))
|
||||||
(define (gulp food-list)
|
(class animal%
|
||||||
(for ([f food-list])
|
(super-new)
|
||||||
(eat f)))))
|
(inherit eat)
|
||||||
(define sloppy-eater%
|
(define/public (gulp food-list)
|
||||||
(class glutton%
|
(for ([f food-list])
|
||||||
(super-new)
|
(eat f)))))
|
||||||
(define/override (eat f)
|
(class glutton%
|
||||||
(let ([food-size (get-field size f)])
|
(super-new)
|
||||||
(set! size (/ food-size 2))
|
(inherit-field size)
|
||||||
(set-field! size f (/ food-size 2))
|
(define/override (eat f)
|
||||||
f))))]
|
(let ([food-size (get-field size f)])
|
||||||
|
(set! size (/ food-size 2))
|
||||||
|
(set-field! size f (/ food-size 2))
|
||||||
|
f)))))]
|
||||||
|
|
||||||
@interaction-eval[
|
@interaction-eval[
|
||||||
#:eval class-eval
|
#:eval class-eval
|
||||||
(begin
|
(define/contract sloppy-eater%
|
||||||
(define/contract glutton%
|
(class/c [eat (->m edible/c edible/c)])
|
||||||
(class/c (override [eat (->m edible/c void?)]))
|
(begin
|
||||||
(class animal%
|
(define/contract glutton%
|
||||||
(super-new)
|
(class/c (override [eat (->m edible/c void?)]))
|
||||||
(inherit eat)
|
(class animal%
|
||||||
(define/public (gulp food-list)
|
(super-new)
|
||||||
(for ([f food-list])
|
(inherit eat)
|
||||||
(eat f)))))
|
(define/public (gulp food-list)
|
||||||
(define sloppy-eater%
|
(for ([f food-list])
|
||||||
|
(eat f)))))
|
||||||
(class glutton%
|
(class glutton%
|
||||||
(super-new)
|
(super-new)
|
||||||
(inherit-field size)
|
(inherit-field size)
|
||||||
|
@ -991,6 +1007,26 @@ this difference:
|
||||||
(get-field size slop1)
|
(get-field size slop1)
|
||||||
(send pig gulp (list slop1 slop2 slop3))]
|
(send pig gulp (list slop1 slop2 slop3))]
|
||||||
|
|
||||||
|
In addition to the internal class contract forms shown here, there are
|
||||||
|
similar forms for Beta-style augmentable methods. The @scheme[inner]
|
||||||
|
form describes to the subclass what is expected from augmentations of
|
||||||
|
a given method. Both @scheme[augment] and @scheme[augride] tell the
|
||||||
|
subclass that the given method is a method which has been augmented and
|
||||||
|
that any calls to the method in the subclass will dynamically
|
||||||
|
dispatch to the appropriate implementation in the superclass. Such
|
||||||
|
calls will be checked according to the given contract. The two forms
|
||||||
|
differ in that use of @scheme[augment] signifies that subclasses can
|
||||||
|
augment the given method, whereas use of @scheme[augride] signifies that
|
||||||
|
subclasses must override the current augmentation instead.
|
||||||
|
|
||||||
|
This means that not all forms can be used at the same time. Only one of the
|
||||||
|
@scheme[override], @scheme[augment], and @scheme[augride] forms can be used
|
||||||
|
for a given method, and none of these forms can be used if the given method
|
||||||
|
has been finalized. In addition, @scheme[super] can be specified for a given
|
||||||
|
method only if @scheme[augride] or @scheme[override] can be specified.
|
||||||
|
Similarly, @scheme[inner] can be specified only if @scheme[augment] or
|
||||||
|
@scheme[augride] can be specified.
|
||||||
|
|
||||||
@; ----------------------------------------------------------------------
|
@; ----------------------------------------------------------------------
|
||||||
|
|
||||||
@close-eval[class-eval]
|
@close-eval[class-eval]
|
||||||
|
|
Loading…
Reference in New Issue
Block a user