6.7 Object and Class Contracts

► |  | The Racket Reference

1 | Language Model
2 | Notation for Documentation
3 | Syntactic Forms
4 | Datatypes
5 | Structures
6 | Classes and Objects
7 | Units
8 | Contracts
9 | Pattern Matching
10 | Control Flow
11 | Concurrency and Parallelism
12 | Macros
13 | Input and Output
14 | Reflection and Security
15 | Operating System
16 | Memory Management
17 | Unsafe Operations
18 | Running Racket
| Bibliography
| Index

▼ | 6 | Classes and Objects

6.1 | Creating Interfaces
6.2 | Creating Classes
6.3 | Creating Objects
6.4 | Field and Method Access
6.5 | Mixins
6.6 | Traits
6.7 | Object and Class Contracts
6.8 | Object Equality and Hashing
6.9 | Object Serialization
6.10 | Object Printing
6.11 | Object, Class, and Interface Utilities
6.12 | Surrogates

On this page:

class/ c
absent
->m
->*m
case->m
->dm
object/ c
instanceof/ c
dynamic-object/ c
object-contract
mixin-contract
make-mixin-contract
is-a?/ c
implementation?/ c
subclass?/ c

top contents ← prev up next →

#### 6.7`` Object and Class Contracts🔗ℹ

> syntax (class/c maybe-opaque member-spec ...)
> maybe-opaque |  | = |  |
> |  | | |  | #:opaque
> |  | | |  | #:opaque #:ignore-local-member-names
> member-spec |  | = |  | method-spec
> |  | | |  | (field field-spec ...)
> |  | | |  | (init field-spec ...)
> |  | | |  | (init-field field-spec ...)
> |  | | |  | (inherit method-spec ...)
> |  | | |  | (inherit-field field-spec ...)
> |  | | |  | (super method-spec ...)
> |  | | |  | (inner method-spec ...)
> |  | | |  | (override method-spec ...)
> |  | | |  | (augment method-spec ...)
> |  | | |  | (augride method-spec ...)
> |  | | |  | (absent absent-spec ...)
> method-spec |  | = |  | method-id
> |  | | |  | (method-id method-contract-expr)
> field-spec |  | = |  | field-id
> |  | | |  | (field-id contract-expr)
> absent-spec |  | = |  | method-id
> |  | | |  | (field field-id ...)

Produces a contract for a class.

There are two major categories of contracts listed in a class/c form: external and internal contracts. External contracts govern behavior when an object is instantiated from a class or when methods or fields are accessed via an object of that class. Internal contracts govern behavior when method or fields are accessed within the class hierarchy. This separation allows for stronger contracts for class clients and weaker contracts for subclasses.

Method contracts must contain an additional initial argument which corresponds to the implicit this parameter of the method. This allows for contracts which discuss the state of the object when the method is called (or, for dependent contracts, in other parts of the contract). Alternative contract forms, such as ->m, are provided as a shorthand for writing method contracts.

Methods and fields listed in an absent clause must not be present in the class.

A class contract can be specified to be opaque with the #:opaque keyword. An opaque class contract will only accept a class that defines exactly the external methods and fields specified by the contract. A contract error is raised if the contracted class contains any methods or fields that are not specified. Methods or fields with local member names (i.e., defined with define-local-member-name) are ignored for this check if #:ignore-local-member-names is provided.

The external contracts are as follows:

- An external method contract without a tag describes the behavior of the implementation of 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.

  If only the field name is present, this is equivalent to insisting only that the method is present in the class.

  Examples:

  > (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)
  > "reach for the sky, #f"
  > > (send (new woody+c%) draw 'zurg)
  > "reach for the sky, zurg"
  > > (send (new woody+c%) draw #f)
  > draw: contract violation
  > expected: symbol?
  > given: #f
  > in: the 1st argument of
  > the draw method in
  > (class/c (draw (->m symbol? string?)))
  > contract from: (definition woody+c%)
  > contract on: woody+c%
  > blaming: top-level
  > (assuming the contract is correct)
  > at: eval:74:0

- An external field contract, tagged with field, describes the behavior of the value contained in that field when accessed from outside the class. Since fields may be mutated, these contracts are checked on any external access (via get-field) and external mutations (via set-field!) of the field.

  If only the field name is present, this is equivalent to using the contract any/c (but it is checked more efficiently).

  Examples:

  > (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%))
  > 'uninitialized
  > > (let ([woody (new woody/hat+c%)])
  > (send woody lose-hat)
  > (get-field hat-location woody))
  > 'lost
  > > (get-field hat-location (new woody/hat+c%))
  > woody/hat+c%: broke its own contract
  > promised: (or/c (quote on-head) (quote lost))
  > produced: 'uninitialized
  > in: the hat-location field in
  > (class/c
  > (draw (->m symbol? string?))
  > (lose-hat (->m void?))
  > (find-hat (->m void?))
  > (field (hat-location
  > (or/c 'on-head 'lost))))
  > contract from: (definition woody/hat+c%)
  > blaming: (definition woody/hat+c%)
  > (assuming the contract is correct)
  > at: eval:79:0
  > > (let ([woody (new woody/hat+c%)])
  > (set-field! hat-location woody 'under-the-dresser))
  > woody/hat+c%: contract violation
  > expected: (or/c (quote on-head) (quote lost))
  > given: 'under-the-dresser
  > in: the hat-location field in
  > (class/c
  > (draw (->m symbol? string?))
  > (lose-hat (->m void?))
  > (find-hat (->m void?))
  > (field (hat-location
  > (or/c 'on-head 'lost))))
  > contract from: (definition woody/hat+c%)
  > blaming: top-level
  > (assuming the contract is correct)
  > at: eval:79:0

- An initialization argument contract, tagged with 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 class/c form is applied to the first value tagged with that name in the list of initialization arguments, and so on.

  If only the initialization argument name is present, this is equivalent to using the contract any/c (but it is checked more efficiently).

  Examples:

  > (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]))
  > 'lost
  > > (get-field hat-location
  > (new woody/init-hat+c%
  > [init-hat-location 'slinkys-mouth]))
  > woody/init-hat+c%: contract violation
  > expected: (or/c (quote on-head) (quote lost))
  > given: 'slinkys-mouth
  > in: the init-hat-location init argument in
  > (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))))
  > contract from:
  > (definition woody/init-hat+c%)
  > blaming: top-level
  > (assuming the contract is correct)
  > at: eval:85:0

- The contracts listed in an init-field section are treated as if each contract appeared in an init section and a field section.

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.

As with the external contracts, when a method or field name is specified but no contract appears, the contract is satisfied merely with the presence of the corresponding field or method.

- A method contract tagged with inherit describes the behavior of the method when invoked directly (i.e., via 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.

  Examples:

  > > (new (class woody+c%
  > (inherit draw)
  > (super-new)
  > (printf "woody sez: “~a”\n" (draw "evil dr porkchop"))))
  > woody sez: “reach for the sky, evil dr porkchop”
  > (object:eval:88:0 ...)
  > (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"))))
  > draw: contract violation
  > expected: symbol?
  > given: "evil dr porkchop"
  > in: the 1st argument of
  > the draw method in
  > (class/c
  > (inherit (draw (->m symbol? string?))))
  > contract from: (definition woody+c-inherit%)
  > contract on: woody+c-inherit%
  > blaming: top-level
  > (assuming the contract is correct)
  > at: eval:89:0

- A method contract tagged with super describes the behavior of method-id when called by the super form in a subclass. This contract only affects super calls in subclasses which call the contract class’s implementation of method-id.

  This example shows how to extend the draw method so that if it is passed two arguments, it combines two calls to the original draw method, but with a contract the controls how the super methods must be invoked.

  Examples:

  > (define/contract woody%+s
  > (class/c (super [draw (->m symbol? string?)]))
  > (class object%
  > (define/public (draw who)
  > (format "reach for the sky, ~a" who))
  > (super-new)))
  > (define woody2+c%
  > (class woody%+s
  > (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)
  > "reach for the sky, evil-dr-porkchop and reach for the sky, zurg"
  > > (send (new woody2+c%) draw "evil dr porkchop" "zurg")
  > draw: contract violation
  > expected: symbol?
  > given: "evil dr porkchop"
  > in: the 1st argument of
  > the draw method in
  > (class/c
  > (super (draw (->m symbol? string?))))
  > contract from: (definition woody%+s)
  > contract on: woody%+s
  > blaming: top-level
  > (assuming the contract is correct)
  > at: eval:91:0

  The last call signals an error blaming woody2%+c because there is no contract checking the initial draw call and the super-call violates its contract.

- A method contract tagged with inner describes the behavior the class expects of an augmenting method in a subclass. This contract affects any implementations of method-id in subclasses which can be called via inner from the contracted class. This means a subclass which implements method-id via augment or overment stop future subclasses from being affected by the contract, since further extension cannot be reached via the contracted class.

- A method contract tagged with override describes the behavior expected by the contracted class for method-id when called directly (i.e. by the application (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).

  This time, instead of overriding draw to support two arguments, we can make a new method, draw2 that takes the two arguments and calls draw. We also add a contract to make sure that overriding draw doesn’t break draw2.

  Examples:

  > (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)
  > draw: contract violation
  > expected: string?
  > given: 'not-a-string
  > in: the range of
  > the draw method in
  > (class/c
  > (override (draw (->m symbol? string?))))
  > contract from:
  > (definition woody2+override/c%)
  > contract on: woody2+override/c%
  > blaming: top-level
  > (assuming the contract is correct)
  > at: eval:95:0

- A method contract tagged with either augment or augride describes the behavior provided by the contracted class for 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 implementation will change the entry point to the dynamic dispatch chain. augment is used when subclasses can augment the method, and augride is used when subclasses can override the current augmentation.

- A field contract tagged with inherit-field describes the behavior of the value contained in that field when accessed directly (i.e., via 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.

- Changed in version 6.1.1.8 of package base: Opaque class/c now optionally ignores local member names if an additional keyword is supplied.

> syntax (absent absent-spec ...)

See class/c; use outside of a class/c form is a syntax error.

> syntax (->m dom ... range)

Similar to ->, except that the domain of the resulting contract contains one more element than the stated domain, where the first (implicit) argument is contracted with any/c. This contract is useful for writing simpler method contracts when no properties of this need to be checked.

> syntax (->*m (mandatory-dom ...) (optional-dom ...) rest range)

Similar to ->*, except that the mandatory domain of the resulting contract contains one more element than the stated domain, where the first (implicit) argument is contracted with any/c. This contract is useful for writing simpler method contracts when no properties of this need to be checked.

> syntax (case->m (-> dom ... rest range) ...)

Similar to case->, except that the mandatory domain of each case of the resulting contract contains one more element than the stated domain, where the first (implicit) argument is contracted with any/c. This contract is useful for writing simpler method contracts when no properties of this need to be checked.

> > syntax
> > (->dm (mandatory-dependent-dom ...)
> > (optional-dependent-dom ...)
> > dependent-rest
> > pre-cond
> > dep-range)

Similar to ->d, except that the mandatory domain of the resulting contract contains one more element than the stated domain, where the first (implicit) argument is contracted with any/c. In addition, this is appropriately bound in the body of the contract. This contract is useful for writing simpler method contracts when no properties of this need to be checked.

> syntax (object/c member-spec ...)
> member-spec |  | = |  | method-spec
> |  | | |  | (field field-spec ...)
> method-spec |  | = |  | method-id
> |  | | |  | (method-id method-contract)
> field-spec |  | = |  | field-id
> |  | | |  | (field-id contract-expr)

Produces a contract for an object.

Unlike the older form object-contract, but like class/c, arbitrary contract expressions are allowed. Also, method contracts for object/c follow those for class/c. An object wrapped with object/c behaves as if its class had been wrapped with the equivalent class/c contract.

> procedure (instanceof/c class-contract) → contract?
> class-contract : contract?

Produces a contract for an object, where the object is an instance of a class that conforms to class-contract.

> > procedure
> > (dynamic-object/c |  | method-names |  |  |  |
> > |  | method-contracts |  |  |  |
> > |  | field-names |  |  |  |
> > |  | field-contracts) |  | → |  | contract?
> method-names : (listof symbol?)
> method-contracts : (listof contract?)
> field-names : (listof symbol?)
> field-contracts : (listof contract?)

Produces a contract for an object, similar to object/c but where the names and contracts for both methods and fields can be computed dynamically. The list of names and contracts for both methods and field respectively must have the same lengths.

> syntax (object-contract member-spec ...)
> member-spec |  | = |  | (method-id method-contract)
> |  | | |  | (field field-id contract-expr)
> method-contract |  | = |  | (-> dom ... range)
> (->* (mandatory-dom ...)
> (optional-dom ...)
> rest
> range)
> (->d (mandatory-dependent-dom ...)
> (optional-dependent-dom ...)
> dependent-rest
> pre-cond
> dep-range)
> dom |  | = |  | dom-expr
> |  | | |  | keyword dom-expr
> range |  | = |  | range-expr
> |  | | |  | (values range-expr ...)
> |  | | |  | any
> mandatory-dom |  | = |  | dom-expr
> |  | | |  | keyword dom-expr
> optional-dom |  | = |  | dom-expr
> |  | | |  | keyword dom-expr
> rest |  | = |  |
> |  | | |  | #:rest rest-expr
> mandatory-dependent-dom |  | = |  | [id dom-expr]
> |  | | |  | keyword [id dom-expr]
> optional-dependent-dom |  | = |  | [id dom-expr]
> |  | | |  | keyword [id dom-expr]
> dependent-rest |  | = |  |
> |  | | |  | #:rest id rest-expr
> pre-cond |  | = |  |
> |  | | |  | #:pre-cond boolean-expr
> dep-range |  | = |  | any
> |  | | |  | [id range-expr] post-cond
> |  | | |  | (values [id range-expr] ...) post-cond
> post-cond |  | = |  |
> |  | | |  | #:post-cond boolean-expr

Produces a contract for an object.

Each of the contracts for a method has the same semantics as the corresponding function contract, but the syntax of the method contract must be written directly in the body of the object-contract—much like the way that methods in class definitions use the same syntax as regular function definitions, but cannot be arbitrary procedures. Unlike the method contracts for class/c, the implicit this argument is not part of the contract. To allow for the use of this in dependent contracts, ->d contracts implicitly bind this to the object itself.

> value mixin-contract : contract?

A function contract that recognizes mixins. It guarantees that the input to the function is a class and the result of the function is a subclass of the input.

> procedure (make-mixin-contract type ...) → contract?
> type : (or/c class? interface?)

Produces a function contract that guarantees the input to the function is a class that implements/subclasses each type, and that the result of the function is a subclass of the input.

> procedure (is-a?/c type) → flat-contract?
> type : (or/c class? interface?)

Accepts a class or interface and returns a flat contract that recognizes objects that instantiate the class/interface.

See is-a?.

> procedure (implementation?/c interface) → flat-contract?
> interface : interface?

Returns a flat contract that recognizes classes that implement interface.

See implementation?.

> procedure (subclass?/c class) → flat-contract?
> class : class?

Returns a flat contract that recognizes classes that are subclasses of class.

See subclass?.

top contents ← prev up next →