From dd96465208ef13d1b2cedbab866a8fa41d483826 Mon Sep 17 00:00:00 2001 From: Stevie Strickland Date: Sat, 27 Feb 2010 21:32:11 +0000 Subject: [PATCH] Okay, finalized initial version of this, which I'll probably go ahead and merge to trunk. svn: r18392 --- collects/scribblings/guide/class.scrbl | 132 ++++++++++++++++--------- 1 file changed, 84 insertions(+), 48 deletions(-) diff --git a/collects/scribblings/guide/class.scrbl b/collects/scribblings/guide/class.scrbl index 19240d7754..8d1e58f42c 100644 --- a/collects/scribblings/guide/class.scrbl +++ b/collects/scribblings/guide/class.scrbl @@ -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 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] -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: +the @scheme[class/c] form is used. The @scheme[class/c] form has many +subforms, which describe two types of contracts on fields and methods: +those that affect uses via instantiated objects and those that affect +subclasses. + +@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[ (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])))) (send bob eat giant)] -There are two important caveats for these contracts, which we will -call @deftech{external class contracts}. First, external method contracts -are only enforced when the target of dynamic dispatch is the method -implementation of the contracted class, which lies within the contract -boundary. Overriding that implementation, and thus changing the target -of dynamic dispatch, will mean that the contract is no longer enforced -for clients, since accessing the method no longer crosses the contract -boundary. Unlike external method contracts, external field contracts -are always enforced for clients of subclasses, since fields cannot be -overridden or shadowed. +There are two important caveats for external class contracts. First, +external method contracts are only enforced when the target of dynamic +dispatch is the method implementation of the contracted class, which +lies within the contract boundary. Overriding that implementation, and +thus changing the target of dynamic dispatch, will mean that the contract +is no longer enforced for clients, since accessing the method no longer +crosses the contract boundary. Unlike external method contracts, external +field contracts are always enforced for clients of subclasses, since fields +cannot be overridden or shadowed. Second, these contracts do not restrict subclasses of @scheme[animal%] in any way. Fields and methods that are inherited and used by subclasses @@ -908,6 +913,8 @@ both caveats: (send elephant eat (new object%)) (get-field size elephant)] +@subsection{Internal Class Contracts} + Notice that retrieving the @scheme[size] field from the object @scheme[elephant] blames @scheme[animal%] for the contract violation. 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%] only store appropriate values within the @scheme[size] field and use the implementation of @scheme[size] from @scheme[animal%] appropriately. -These contract forms only affect uses within the class hierarchy, so the -@scheme[override] form does not automatically enter subclasses into -obligations when objects of those classes are used. Instead, such contracts -are only checked when uses of the method within the superclass dynamically -dispatch to the subclass's implementation. The following example shows -this difference: +These contract forms only affect uses within the class hierarchy, and only +for method calls that cross the contract boundary. + +That means that @scheme[inherit] will only affect subclass uses of a method +until a subclass overrides that method, and that @scheme[override] only +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[ -(define/contract glutton% - (class/c (override [eat (->m edible/c void?)])) - (class animal% - (super-new) - (inherit eat) - (define (gulp food-list) - (for ([f food-list]) - (eat f))))) -(define sloppy-eater% - (class glutton% - (super-new) - (define/override (eat f) - (let ([food-size (get-field size f)]) - (set! size (/ food-size 2)) - (set-field! size f (/ food-size 2)) - f))))] +(define/contract sloppy-eater% + (class/c [eat (->m edible/c edible/c)]) + (begin + (define/contract glutton% + (class/c (override [eat (->m edible/c void?)])) + (class animal% + (super-new) + (inherit eat) + (define/public (gulp food-list) + (for ([f food-list]) + (eat f))))) + (class glutton% + (super-new) + (inherit-field size) + (define/override (eat f) + (let ([food-size (get-field size f)]) + (set! size (/ food-size 2)) + (set-field! size f (/ food-size 2)) + f)))))] @interaction-eval[ #:eval class-eval -(begin - (define/contract glutton% - (class/c (override [eat (->m edible/c void?)])) - (class animal% - (super-new) - (inherit eat) - (define/public (gulp food-list) - (for ([f food-list]) - (eat f))))) - (define sloppy-eater% +(define/contract sloppy-eater% + (class/c [eat (->m edible/c edible/c)]) + (begin + (define/contract glutton% + (class/c (override [eat (->m edible/c void?)])) + (class animal% + (super-new) + (inherit eat) + (define/public (gulp food-list) + (for ([f food-list]) + (eat f))))) (class glutton% (super-new) (inherit-field size) @@ -991,6 +1007,26 @@ this difference: (get-field size slop1) (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]