From 67d6e41b25930a7ba097009a8e151c6706def594 Mon Sep 17 00:00:00 2001 From: Robby Findler Date: Sat, 27 Apr 2013 16:57:21 -0500 Subject: [PATCH] add some examples to the class/c contract docs and fix some commas --- collects/scribblings/reference/class.scrbl | 179 ++++++++++++++++++--- 1 file changed, 157 insertions(+), 22 deletions(-) diff --git a/collects/scribblings/reference/class.scrbl b/collects/scribblings/reference/class.scrbl index 44cb258701..5c964d42ad 100644 --- a/collects/scribblings/reference/class.scrbl +++ b/collects/scribblings/reference/class.scrbl @@ -71,7 +71,7 @@ ) -@(interaction-eval #:eval class-eval (require racket/class)) +@(interaction-eval #:eval class-eval (require racket/class racket/contract)) @(interaction-eval #:eval class-ctc-eval (require racket/class racket/contract)) @@ -1788,46 +1788,143 @@ not specified. The external contracts are as follows: @itemize[ - @item{A method contract without a tag describes the behavior + @item{An external method contract without a tag describes the behavior of the implementation of @racket[method-id] on method sends to an object of the contracted class. This contract will continue to be checked in subclasses until the contracted class's implementation is - no longer the entry point for dynamic dispatch.} - @item{A field contract, tagged with @racket[field], describes the + no longer the entry point for dynamic dispatch. + + @defexamples[#:eval + class-eval + (define woody% + (class object% + (define/public (draw who) + (format "reach for the sky, ~a" who)) + (super-new))) + + (define/contract woody+c% + (class/c [draw (->m symbol? string?)]) + woody%) + + (send (new woody%) draw #f) + (send (new woody+c%) draw 'zurg) + (send (new woody+c%) draw #f)] + } + @item{An external field contract, tagged with @racket[field], describes the behavior of the value contained in that field when accessed via an object of that class. Since fields may be mutated, these contracts - are checked on any external access and/or mutation of the field.} + are checked on any external access and/or mutation of the field. + + @defexamples[#:eval + class-eval + (define woody/hat% + (class woody% + (field [hat-location 'uninitialized]) + (define/public (lose-hat) (set! hat-location 'lost)) + (define/public (find-hat) (set! hat-location 'on-head)) + (super-new))) + (define/contract woody/hat+c% + (class/c [draw (->m symbol? string?)] + [lose-hat (->m void?)] + [find-hat (->m void?)] + (field [hat-location (or/c 'on-head 'lost)])) + woody/hat%) + + (get-field hat-location (new woody/hat%)) + (let ([woody (new woody/hat+c%)]) + (send woody lose-hat) + (get-field hat-location woody)) + (get-field hat-location (new woody/hat+c%))] + + } @item{An initialization argument contract, tagged with @racket[init], describes the expected behavior of the value paired with that name during class instantiation. The same name can be provided more than once, in which case the first such contract in the @racket[class/c] form is applied to the first value tagged with that name in the list - of initialization arguments, and so on.} + of initialization arguments, and so on. + + @defexamples[#:eval + class-eval + (define woody/init-hat% + (class woody% + (init init-hat-location) + (field [hat-location init-hat-location]) + (define/public (lose-hat) (set! hat-location 'lost)) + (define/public (find-hat) (set! hat-location 'on-head)) + (super-new))) + (define/contract woody/init-hat+c% + (class/c [draw (->m symbol? string?)] + [lose-hat (->m void?)] + [find-hat (->m void?)] + (init [init-hat-location (or/c 'on-head 'lost)]) + (field [hat-location (or/c 'on-head 'lost)])) + woody/init-hat%) + (get-field hat-location (new woody/init-hat+c% [init-hat-location 'lost])) + (get-field hat-location (new woody/init-hat+c% [init-hat-location 'slinkys-mouth]))] + + } @item{The contracts listed in an @racket[init-field] section are treated as if each contract appeared in an @racket[init] section and a @racket[field] section.} ] -The internal contracts are as follows: +The internal contracts restrict the behavior of method calls +made between classes and their subclasses; such calls are not +controlled by the class contracts described above. @itemize[ - @item{A method contract, tagged with @racket[inherit], describes the + @item{A method contract tagged with @racket[inherit] describes the behavior of the method when invoked directly (i.e., via @racket[inherit]) in any subclass of the contracted class. This contract, like external method contracts, applies until the contracted class's method implementation is no longer the entry point - for dynamic dispatch.} - @item{A field contract, tagged with @racket[inherit-field], describes - the behavior of the value contained in that field when accessed - directly (i.e., via @racket[inherit-field]) in any subclass of the - contracted class. Since fields may be mutated, these contracts are - checked on any access and/or mutation of the field that occurs in - such subclasses.} - @item{A method contract, tagged with @racket[super], describes the behavior of + for dynamic dispatch. + + @defexamples[#:eval + class-eval + (new (class woody+c% + (inherit draw) + (super-new) + (printf "woody sez: “~a”\n" (draw "evil dr porkchop")))) + (define/contract woody+c-inherit% + (class/c (inherit [draw (->m symbol? string?)])) + woody+c%) + (new (class woody+c-inherit% + (inherit draw) + (printf "woody sez: ~a\n" (draw "evil dr porkchop"))))] + + } + @item{A method contract tagged with @racket[super] describes the behavior of @racket[method-id] when called by the @racket[super] form in a subclass. This contract only affects @racket[super] calls in subclasses which call the contract class's implementation of - @racket[method-id].} - @item{A method contract, tagged with @racket[inner], describes the + @racket[method-id]. + + This example shows how to extend the @racket[draw] method + so that if it is passed two arguments, it combines two + calls to the original @racket[draw] method, but with a + contract the controls how the @racket[super] methods must + be invoked. + + @defexamples[#:eval + class-eval + (define/contract woody2+c% + (class/c (super [draw (->m symbol? string?)])) + (class woody% + (define/override draw + (case-lambda + [(a) (super draw a)] + [(a b) (string-append (super draw a) + " and " + (super draw b))])) + (super-new))) + (send (new woody2+c%) draw 'evil-dr-porkchop 'zurg) + (send (new woody2+c%) draw "evil dr porkchop" "zurg")] + + The last call signals an error blaming @racket[woody2%] because + there is no contract checking the initial @racket[draw] call. + } + @item{A method contract tagged with @racket[inner] describes the behavior the class expects of an augmenting method in a subclass. This contract affects any implementations of @racket[method-id] in subclasses which can be called via @racket[inner] from the contracted @@ -1835,14 +1932,45 @@ The internal contracts are as follows: @racket[augment] or @racket[overment] stop future subclasses from being affected by the contract, since further extension cannot be reached via the contracted class.} - @item{A method contract, tagged with @racket[override], describes the + @item{A method contract tagged with @racket[override] describes the behavior expected by the contracted class for @racket[method-id] when called directly (i.e. by the application @racket[(method-id ...)]). This form can only be used if overriding the method in subclasses will change the entry point to the dynamic dispatch chain (i.e., the - method has never been augmentable).} - @item{A method contract, tagged with either @racket[augment] or - @racket[augride], describes the behavior provided by the contracted + method has never been augmentable). + + This time, instead of overriding @racket[draw] to support + two arguments, we can make a new method, @racket[draw2] that + takes the two arguments and calls @racket[draw]. We also + add a contract to make sure that overriding @racket[draw] + doesn't break @racket[draw2]. + + @defexamples[#:eval + class-eval + (define/contract woody2+override/c% + (class/c (override [draw (->m symbol? string?)])) + (class woody+c% + (inherit draw) + (define/public (draw2 a b) + (string-append (draw a) + " and " + (draw b))) + (super-new))) + + (define woody2+broken-draw + (class woody2+override/c% + (define/override (draw x) + 'not-a-string) + (super-new))) + + (send (new woody2+broken-draw) draw2 + 'evil-dr-porkchop + 'zurg)] + + + } + @item{A method contract tagged with either @racket[augment] or + @racket[augride] describes the behavior provided by the contracted class for @racket[method-id] when called directly from subclasses. These forms can only be used if the method has previously been augmentable, which means that no augmenting or overriding @@ -1850,6 +1978,13 @@ The internal contracts are as follows: chain. @racket[augment] is used when subclasses can augment the method, and @racket[augride] is used when subclasses can override the current augmentation.} + @item{A field contract tagged with @racket[inherit-field] describes + the behavior of the value contained in that field when accessed + directly (i.e., via @racket[inherit-field]) in any subclass of the + contracted class. Since fields may be mutated, these contracts are + checked on any access and/or mutation of the field that occurs in + such subclasses.} + ]} @defform[(absent method-id ...)]{