On this page:
6.1 Contract syntax
6.2 Contract combinators
Any  C
None  C
Vec  C
Fun  C
Not  C
Or  C
And  C
Int  In  C
apply_  contract
7.4

6 Contracts

The contract system helps guard parts of a program against each other by enforcing properties of function and method parameters and results, structure and class fields, and variables. In particular, contracts specify properties of values that are checked at various points in the program.

A contract may be flat, in which case it is checked immediately against a value. Or a contract may by higher-order, in which case it may have to wrap a value to perform further checking in the future.

A number of DSSL2 values may be used as contracts, including:

For example, to ensure that a function takes a string and returns a number or False, we could use the contract FunC[str?, OrC(num?, False)].

6.1 Contract syntax

compound

let var_name : ⟨ctc⟩ = ⟨expr

Binds variable var_name to the value of expression ⟨expr⟩, while applying the contract ⟨ctc⟩. Subsequent assignments to the variable also must satisfy the contract. For example:

let x : int? = 0

Note that the ⟨exprrhs⟩ is optional, and the contract will not be checked before the variable is assigned:

let x : int?
 
x = 5

compound

def namef(name1: ⟨ctc1⟩, ..., namek: ⟨ctck⟩) -> ⟨ctcres⟩: ⟨block

Defines function namef while specifying contract expressions ⟨ctc1⟩ through ⟨ctck⟩ for the parameters, and contract expression ⟨ctcres⟩ for the result. For example:

def pythag(x: num?, y: num?) -> num?:
    sqrt(x * x + y * y)

Each of the contract positions is optional, and if omitted defaults to AnyC.

Optionally, after namef and before the left parenthesis, ⟨opt_cvars⟩ may appear: one or more comma-separated names, enclosed in square brackets. These are optional contract parameters to the function, which can appear in the contracts on the rest of the parameters, the result, and anywhere in the body of the function.

For example, a vector mapping function might be defined with contract parameters and contracts on its parameters as follows:

def vec_map[T, U](f: FunC[T, U], v: VecC[T]) -> VecC[U]:
    [ f(e) for e in v ]

When calling vec_map, it’s possible to supply actual contracts for T and U in square brackets; or omitting the square brackets, the contracts both default to AnyC.

compound

struct name:

    let field_name1: ⟨ctc1

    ...

    let field_namek: ⟨ctck

Defines a structure name with the given contracts ⟨ctci⟩ applied to the fields field_namei. This means that the contracts will be applied both when constructing the structure and when mutating it. For example:

struct posn:
    let x: float?
    let y: float?

Now constructing a posn will require both parameters to satisfy the float? predicate, as will assigning to either field.

It’s possible to include contracts on some fields without including them on all, and the fields with omitted contracts default to AnyC.

compound

class name [ [ { cvar },* ] ] [ ( { interface_name },* ) ]:

    let field_name1: ⟨ctcfield_1

    ...

    let field_namek: ⟨ctcfield_k

    def meth_name0(self0 { , arg_name0: ctcarg_0 }*) -> ⟨ctcres_0⟩: ⟨block0

    ...

    def meth_namen(selfn { , arg_namen: ctcarg_n }*) -> ⟨ctcres_n⟩: ⟨blockn

Defines a class with contracts. See class for the basics of classes.

Contracts may be placed on:

Any contracts may be omitted, and default to AnyC. No contract may be placed on a method’s self parameter, as that parameter is already known to be an instance of the class.

If the class implements interfaces that have contracts, the interfaces’ contracts have no effect on the defined class.

A class may have some number of generic contract parameters, cvar. These can be used to parameterize a class over other contracts. When provided, they are in scope throughout the class. The external constructor receives the actual contracts for an instance of the class as optional, square bracket parameters, giving before the ordinary parameters that are passed to the internal constructor.

For example, we can define a generic position class:

class Posn[T]:
    let _x: T
    let _y: T
 
    def __init__(self, x: T, y: T):
        self._x = x
        self._y = y
 
    def x(self) -> T:
        self._x
 
    def y(self) -> T:
        self._y

Now it is possible to make a position with int? coordinates or a position with float? coordinates, by passing the coordinate contract in square brackets to the Posn constructor:

let p = Posn[int?](3, 4)
let q = Posn[float?](3.0, 4.0)

Specifying contract parameters is optional, and if omitted, they default to AnyC. So we can write

let r = Posn(3, 4.0)

to get a Posn[AnyC].

When a contract parameter is given, the Posn constructor and methods all check the given values against the given contract.

When a class has generic contract parameters, its predicate can also be instantiated with contracts, using square brackets:

assert Posn?[int?](p)
assert not Posn?[int?](q)
assert not Posn?[int?](r)
 
assert not Posn?[float?](p)
assert Posn?[float?](q)
assert not Posn?[float?](r)

If the predicate is not instantiated, it recognizes all objects of that class regardless of how their contract parameters are instantiated:

assert Posn?(p)
assert Posn?(q)
assert Posn?(r)

Note that a predicate not being instantated is different from instantiating it with AnyC:

assert not Posn?[AnyC](p)
assert not Posn?[AnyC](q)
assert Posn?[AnyC](r)

compound

interface name [ [ { cvar },* ] ]:

    def meth_name1(self1 { , arg_name1: ctcarg_1 }*) -> ⟨ctcres_1

    ...

    def meth_namek(selfn { , arg_namek: ctcarg_n }*) -> ⟨ctcres_k

Defines a interface with contracts. See interface for the basics of interfaces.

As with a class, contracts may be provided on method parameters (except for self) and results. The contracts have no effect when a class implements the interface, but do have an effect with the interface is used to protect an instance of a class that implements it.

Defining an interface name binds three identifiers: name, name?, and name!. The first of these is used in class definitions to refer to the interface; the second is the predicate, which recognizes instances of classes that implement the interface. The third is the interface contract, which can be used to protect instances of classes that implement the interface, to ensure that their usage is only via the interface.

When an interface contract protects an object, first it checks the class of the object. If the class is not declared to implement the interface, an error is signaled. If the class does implement the interface, then a protected object is created. The protected object is like the original object, except that only the methods of the interface are usable; all other methods will error, blaming the caller, if called. Furthermore, any contracts specified on methods in the interface are applied to those methods of the protected object. (Reprotecting a protected object with the same interface has no effect.)

Here is an example of an interface with one method and a class that implements it:

interface HAS_X:
    def get_x(self)
 
class Posn (HAS_X):
    let x
    let y
 
    def __init__(self, x, y):
        self.x = x
        self.y = y
 
    def get_x(self): self.x
    def get_y(self): self.y

We might want to ensure that some client code uses an instance of Posn only according to the HAS_X interface, without accessing its other methods. We can use the HAS_X! interface contract to protect a Posn instance in exactly this way. First, we create an object and use it normally:

let original = Posn(3, 4)
 
assert original.get_x() == 3
assert original.get_y() == 4
 
assert HAS_X?(original)

Note that the interface predicate HAS_X? answers True for instances of classes that implement HAS_X.

We can protect the original object by applying the HAS_X! interface contract, as follows:

let protected: HAS_X! = original

Now, we can call protected.get_x() because interface HAS_X defines the get_x method. But we cannot call get_y on protected, because protecting it with HAS_X! makes all methods other than get_x raise an error:

assert protected.get_x() == 3
assert_error protected.get_y(), \
    'interface HAS_X is protecting method get_y'

We can still access get_y on original:

assert original.get_x() == 3
assert original.get_y() == 4

As another example of how interface contracts can protect objects against misuse, consider the following class Counter, which implements an interface STEPPABLE:

interface STEPPABLE:
    def step(self)
 
class Counter (STEPPABLE):
    let count
    def __init__(self, value: int?): self.count = value
    def get(self): self.count
    def step(self): self.count = self.count + 1

The STEPPABLE! interface contract can be placed on a parameter to a function to ensure that the function only uses the given object according to the STEPPABLE interface. For example, we know that this advance function can only use the step method to advance the Counter:

def advance(counter: STEPPABLE!, count: nat?):
    while count > 0:
        counter.step()
        count = count - 1

A version of the function that attempts to do addition will fail because the necessary methods aren’t usable when the object is protected:

def sneaky_advance(counter: STEPPABLE!, count: nat?):
    # Won't work because both .__init__ and .get are missing.
    counter.__init__(counter.get() + count)

Like classes, interfaces can have generic contract parameters cvar. When an interface has generic contract parameters, these parameters are available to the contracts in the body of the interface. The interface contract name! takes its contract parameters as optional square bracket parameters that default to AnyC.

For example, here is a generic interface for a queue:

interface QUEUE[T]:
    def empty(self) -> bool?
    def enqueue(self, value: T) -> NoneC
    def dequeue(self) -> OrC(False, T)

This interface definition binds three identifiers:

expr

expr0⟩[⟨expr1⟩, ..., ⟨exprk⟩]

Indexes or instantiates the result of ⟨expr0⟩.

If ⟨expr0⟩ evalutes to a vector or string, then k must equal 1, and this is an indexing expression.

If ⟨expr0⟩ is a generic function, class, or contract, then ⟨expr1⟩, ..., ⟨exprk⟩ are the parameters used to instantiate the generic function, class, or contract. For example, function contracts are created using FunC and square brackets:

FunC[int?, char?, str?]

Or here is an example of a generic pair class, which can be instantiated to particular contracts for its components using square brackets:

class Pair[T, U]:
    let _fst
    let _snd
 
    def __init__(self, fst: T, snd: U):
        self._fst = fst
        self._snd = snd
 
    def fst(self) -> T: self._fst
    def snd(self) -> U: self._snd
 
let p = Pair[int?, str?](5, 'six')

6.2 Contract combinators

constant

AnyC: contract?

A flat contract that accepts any value.

constant

NoneC: contract?

A flat contract that accepts the value None, which is the result of pass and other statements that return no value (such as assignment and loops). This is used as the result contract for functions and methods that do not return a value.

procedure

VecC[contract?]: contract?

Creates a contract that protects a vector to ensure that its elements satisfy the given contract.

For example:

let v: VecC[int?] = [2, 3, 4]

Note that vector contracts are checked lazily, when elements are indexed or assigned, rather than eagerly when first protected. So for example:

let v: VecC[int?] = [2, 3, 'four'] # okay, not checked yet
assert v[1] == 3                   # passes check
assert_error v[2]                  # fails check

Assigning a non-integer to an element of v is an error as well.

If the optional square bracket parameter is omitted, VecC just checks that the protected value is a vector.

procedure

FunC[contract?, ..., contract?]: contract?

Creates a function contract with the given arguments and result. The last argument is a contract applied to the result, and all the other arguments are contracts applied to the parameters.

If the optional square bracket parameters are omitted, the resulting contract checks for a procedure, but nothing further.

procedure

NotC(contract?) -> contract?

Creates a contract that inverts the sense of the given flat contract.

procedure

OrC(contract?, contract?, ...) -> contract?

Creates a contract that accepts a value if any of the arguments does. For the details of how this works for higher-order contracts, see racket:or/c.

procedure

AndC(contract?, contract?, ...) -> contract?

Creates a contract that accepts a value if all of the arguments do. For the details of how this works for higher-order contracts, see racket:and/c.

procedure

IntInC(low: OrC(int?, False), high: OrC(int?, False)) -> contract?

Constructs a contract that accepts integers in the closed interval [low, high]. If either end of the interval is False, that end of the interval is unchecked.

procedure

apply_contract(contract?, AnyC) -> AnyC

apply_contract(contract?, AnyC, pos: str?) -> AnyC

apply_contract(contract?, AnyC, pos: str?, neg: str?) -> AnyC

Applies a contract to a value, optionally specifying the parties.

You are unlikely to need to use this.