5.7 Object and Class Contracts
syntax
(class/c maybe-opaque member-spec ...)
maybe-opaque =
| #:opaque 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) field-spec = field-id | (field-id contract-expr) absent-spec = method-id | (field field-id ...)
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 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.
The external contracts are as follows:
A 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.
A field contract, tagged with 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.
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.
The contracts listed in an init-field section are treated as if each contract appeared in an init section and a field section.
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.
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.
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.
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).
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.
syntax
(absent method-id ...)
syntax
(->m dom ... range)
syntax
(->*m (mandatory-dom ...) (optional-dom ...) rest range)
syntax
(case->m (-> dom ... rest range) ...)
syntax
(->dm (mandatory-dependent-dom ...) (optional-dependent-dom ...) dependent-rest pre-cond dep-range)
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)
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?
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
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—
value
procedure
(make-mixin-contract type ...) → contract?
type : (or/c class? interface?)
procedure
(is-a?/c type) → flat-contract?
type : (or/c class? interface?)
procedure
(implementation?/c interface) → flat-contract?
interface : interface?
procedure
(subclass?/c class) → flat-contract?
class : class?