Chapter 13

contract.ss: Contracts

MzLib's contract.ss library defines new forms of expression that specify contracts and new forms of expression that attach contracts to values.

This section describes three classes of contracts: contracts for flat values (described in section 13.1), contracts for functions (described in section 13.2), and contracts for objects and classes (described in section 13.3).

In addition, this section describes how to establishing a contract on a value (in section 13.4).

13.1  Flat Contracts

A contract for a flat value can be a predicate that accepts the value and returns a boolean indicating if the contract holds.

(flat-contract predicate)      FLAT-CONTRACT

Constructs a contract from predicate.

(flat-named-contract type-name predicate)      FLAT-CONTRACT

For better error reporting, a flat contract can be constructed with flat-named-contract, a procedure that accepts two arguments. The first argument must be a string that describes the type that the predicate checks for. The second argument is the predicate itself.

any?      FLAT-CONTRACT

any? is a flat contract that accepts any value.

If you are using this predicate as the result portion of a function contract, consider using any instead. It behaves the same, but in that one restrictive context has better memory performance.

(union contract ···)      CONTRACT

union accepts any number of predicates and at most one function contract and returns a contract that corresponds to the union of them all. If all of the arguments are predicates or flat contracts, it returns a flat contract.

(and/c contract ···)      CONTRACT

and/c accepts any number of contracts and returns a contract that checks that all of the argument contracts hold. If all of the arguments are flat contracts, and/c produces a flat contract.

(not/f flat-contract)      FLAT-CONTRACT

not/f accepts a flat contracts (or a predicate which is implicitly converted to a flat contracts via flat-contract) and returns a flat contract that checks the reverse of the argument.

(>=/c number)      FLAT-CONTRACT

>=/c accepts a number and returns a flat contract that requires the input to be a number and greater than or equal to the original input.

(<=/c number)      FLAT-CONTRACT

<=/c accepts a number and returns a flat contract that requires the input to be a number and less than or equal to the original input.

(>/c number)      FLAT-CONTRACT

>/c accepts a number and returns a flat contract that requires the input to be a number and greater than the original input.

(</c number)      FLAT-CONTRACT

</c accepts a number and returns a flat contract that requires the input to be a number and less than the original input.

(integer-in number number)      FLAT-CONTRACT

integer-in accepts two numbers and returns a flat contract that recognizes if integers between the two inputs, or equal to one of its inputs.

(real-in number number)      FLAT-CONTRACT

real-in accepts two numbers and returns a flat contract that recognizes real numbers between the two inputs, or equal to one of its inputs.

natural-number?      FLAT-CONTRACT

natural-number? is a contract that recognizes natural numbers (i.e., an integer that is either positive or zero).

(string/len number)      FLAT-CONTRACT

string/len accepts a number and returns a flat contract that recognizes strings that have fewer than that number of characters.

false?      FLAT-CONTRACT

false? is a flat contract that recognizes #f.

printable?      FLAT-CONTRACT

printable? is a flat contract that recognizes values that can be written out and read back in with write and read.

(symbols symbol ···1)      FLAT-CONTRACT

symbols accepts any number of symbols and returns a flat contract that recognizes for those symbols.

(is-a?/c class-or-interface)      FLAT-CONTRACT

is-a?/c accepts a class or interface and returns a flat contract that recognizes if objects are subclasses of the class or implement the interface.

(implementation?/c interface)      FLAT-CONTRACT

implementation?/c accepts an interface and returns a flat contract that recognizes if classes are implement the given interface.

(subclass?/c class)      FLAT-CONTRACT

subclass?/c accepts a class and returns a flat-contract that recognizes classes that are subclasses of the original class.

(listof flat-contract)      FLAT-CONTRACT

listof accepts a flat contract (or a predicate which is converted to a flat contract) and returns a flat contract that checks for lists whose elements match the original flat contract.

(list-immutableof contract)      CONTRACT

list-immutableof accepts a contract (or a predicate which is converted to a flat contract) and returns a contract that checks for immutable lists whose elements match the original contract. In contrast to listof, list-immutableof accepts arbitrary contracts, not just flat contracts.

Beware, however, that when a value is applied to this contract, the result will not be eq? to the input.

(vectorof flat-contract)      FLAT-CONTRACT

vectorof accepts a flat contract (or a predicate which is converted to a flat contract via flat-contract) and returns a predicate that checks for vectors whose elements match the original flat contract.

(vector-immutableof contract)      CONTRACT

vector-immutableof accepts a contract (or a predicate which is converted to a flat contract) and returns a contract that checks for immutable lists whose elements match the original contract. In contrast to vectorof, vector-immutableof accepts arbitrary contracts, not just flat contracts.

Beware, however, that when a value is applied to this contract, the result will not be eq? to the input.

(vector/p flat-contract ···)      FLAT-CONTRACT

vector/p accepts any number of flat contracts (or predicates which are converted to flat contracts via flat-contract) and returns a flat-contract that recognizes vectors. The number of elements in the vector must match the number of arguments supplied to vector/p and the elements of the vector must match the corresponding flat contracts.

(vector-immutable/c contract ···)      CONTRACT

vector-immutable/c accepts any number of contracts (or predicates which are converted to flat contracts via flat-contract) and returns a contract that recognizes vectors. The number of elements in the vector must match the number of arguments supplied to vector-immutable/c and the elements of the vector must match the corresponding contracts.

In contrast to vector/p, vector-immutable/c accepts arbitrary contracts, not just flat contracts. Beware, however, that when a value is applied to this contract, the result will not be eq? to the input.

(box/p flat-contract)      FLAT-CONTRACT

box/p accepts a flat contract (or predicate that is converted to a flat contract via flat-contract) and returns a flat contract that recognizes for boxes whose contents match box/p's argument.

(box-immutable/c contract)      CONTRACT

box-immutable/c accepts any number of contracts (or predicates which are converted to flat contracts via flat-contract) and returns a contract that recognizes vectors. The number of elements in the vector must match the number of arguments supplied to box-immutable/c and the elements of the vector must match the corresponding contracts.

In contrast to box/p, box-immutable/c accepts arbitrary contracts, not just flat contracts. Beware, however, that when a value is applied to this contract, the result will not be eq? to the input.

(cons/p flat-contract flat-contract)      FLAT-CONTRACT

cons/p accepts two flat contracts (or predicates that are converted to flat contracts via flat-contract) and returns a flat contract that recognizes cons cells whose car and cdr correspond to cons/p's two arguments.

(cons-immutable/c contract contract)      CONTRACT

cons-immutable/c accepts two contracts (or predicates that are converted to flat contracts via flat-contract) and returns a contract that recognizes immutable cons cells whose car and cdr correspond to cons-immutable/c's two arguments. In contrast to cons/p, cons-immutable/c accepts arbitrary contracts, not just flat contracts.

Beware, however, that when a value is applied to this contract, the result will not be eq? to the input.

(list/p flat-contract ···)      FLAT-CONTRACT

list/p accepts an arbitrary number of flat contracts (or predicates that are converted to flat contracts via flat-contract) and returns a flat contract that recognizes for lists whose length is the same as the number of arguments to list/p and whose elements match those arguments.

(list-immutable/c flat-contract ···)      CONTRACT

list-immutable/c accepts an arbitrary number of contracts (or predicates that are converted to flat contracts via flat-contract) and returns a contract that recognizes for lists whose length is the same as the number of arguments to list-immutable/c and whose elements match those arguments.

In contrast to list/p, list-immutable/c accepts arbitrary contracts, not just flat contracts. Beware, however, that when a value is applied to this contract, the result will not be eq? to the input.

(flat-rec-contract name flat-contract ···)      SYNTAX

Each flat-rec-contract form constructs a flat recursive contract. The first argument is the name of the contract and the following arguments are flat contract expressions that may refer to name.

As an example, this contract:

   (flat-rec-contract sexp
     (cons/c sexp sexp)
     number?
     symbol?)

is a flat contract that checks for (a limited form of) s-expressions. It says that an sexp is either two sexp combined with cons, or a number, or a symbol.

(flat-murec-contract ([name flat-contract ···] ···) body ···)      SYNTAX

The flat-murec-contract form is a generalization of flat-rec-contracts for defining several mutually recursive flat contracts simultaneously.

Each of the names is visible in the entire flat-murec-contract and the result of the final body expression is the result of the entire form.

(anaphoric-contracts)      CONTRACT CONTRACT

(anaphoric-contracts 'equal)      CONTRACT CONTRACT

Returns two linked anaphoric contracts. The first allows all values, and the second only allows values that the first has previously seen.

13.2  Function Contracts

->

This section describes the contract constructors for function contracts. This is their shape:

contract-expr ::==
 | (case-> arrow-contract-expr ···)
 | arrow-contract-expr

arrow-contract-expr ::== 
 | (-> expr ··· expr)
 | (-> expr ··· any)
 | (-> expr ··· (values expr ···))

 | (->* (expr ···) (expr ···))
 | (->* (expr ···) any)
 | (->* (expr ···) expr (expr ···))
 | (->* (expr ···) expr any)

 | (->d expr ··· expr)
 | (->d* (expr ···) expr)
 | (->d* (expr ···) expr expr)

 | (->r ((id expr) ···) expr)
 | (->r ((id expr) ···) any)
 | (->r ((id expr) ···) (values (id expr) ···))
 | (->r ((id expr) ···) id expr expr)
 | (->r ((id expr) ···) id expr any)
 | (->r ((id expr) ···) id expr (values (id expr) ···))

 | (opt-> (expr ···) (expr ···) expr)
 | (opt->* (expr ···) (expr ···) any)
 | (opt->* (expr ···) (expr ···) (expr ···))

where expr is any expression.

(-> expr ···)      SYNTAX

(-> expr ···any)      SYNTAX

The -> contract is for functions that accept a fixed number of arguments and return a single result. The last argument to -> is the contract on the result of the function and the other arguments are the contracts on the arguments to the function. Each of the arguments to -> must be another contract expression or a predicate. For example, this expression:

(integer? boolean? . -> . integer?)

is a contract on functions of two arguments. The first must be an integer and the second a boolean and the function must return an integer. (This example uses MzScheme's infix notation so that the -> appears in a suggestive place; see section 14.3 in PLT MzScheme: Language Manual).

If any is used as the last argument to ->, no contract checking is performed on the result of the function, and tail-recursion is preserved. Except for the memory performance, this is the same as using any? in the result.

The final case of -> expressions treats values as a local keyword - that is, you may not return multiple values to this position, instead if the word values syntactically appears in the in the last argument to -> the function is treated as a multiple value return (this is a shorthand for the two argument variant on ->*).

(->* (expr ···) (expr ···))      SYNTAX

(->* (expr ···) any)      SYNTAX

(->* (expr ···) expr (expr ···))      SYNTAX

(->* (expr ···) expr any)      SYNTAX

The ->* expression is for functions that return multiple results and/or have rest arguments. If two arguments are supplied, the first is the contracts on the arguments to the function and the second is the contract on the results of the function. If three arguments are supplied, the first argument contains the contracts on the arguments to the function (excluding the rest argument), the second contains the contract on the rest argument to the function and the final argument is the contracts on the results of the function. The final argument can be any which, like -> means that no contract is enforced on the result of the function and tail-recursion is preserved.

(->d expr ···)      SYNTAX

(->d* (expr ···) expr))      SYNTAX

(->d* (expr ···) expr expr)      SYNTAX

The ->d and ->d* contract constructors are like their d-less counterparts, except that the result portion is a function that accepts the original arguments to the function and returns the range contracts. The range contract function for ->d* must return multiple values: one for each result of the original function. As an example, this is the contract for sqrt:

(number? 
 . ->d .
 (lambda (in)
   (lambda (out)
     (and (number? out)
          (abs (- (* out out) in) 0.01)))))

It says that the input must be a number and that the difference between the square of the result and the original number is less than 0.01.

(->r ([id expr] ···) expr)      SYNTAX

The ->r contract allows you to build a contract where the arguments to a function may all depend on each other and the result of the function may depend on all of the arguments.

Each of the ids names one of the actual arguments to the function with the contract. Each of the names is available to all of the other contracts. For example, to define a function that accepts three arguments where the second argument and the result must both be between the first, you might write:

(->r ([x number?] [y (and/c (>=/c x) (<=/c z))] [z number?])
     (and/c number? (>=/c x) (<=/c z)))

(->r ([id expr] ···) any)      SYNTAX

This variation on ->r does not check anything about the result of the function, which preserves tail recursion.

(->r ([id expr] ···) (values [id expr] ...))      SYNTAX

This variation on ->r allows multiple value return values. The ids for the domain are bound in all of the exprs, but the ids for the range (the ones inside values) are only bound in the exprs inside the values.

As an example, this contract:

(->r () (values [x number?]
                [y (and/c (>=/c x) (<=/c z))]
                [z number?]))

matches functions that accept no arguments and that return three numberic values that are in ascending order.

(->r ([id expr] ···) id expr expr)      SYNTAX

(->r ([id expr] ···) id expr any)      SYNTAX

(->r ([id expr] ···) id expr (values [id expr] ...))      SYNTAX

These three forms of the ->r contract are just like the previous ones, except that the functions they matches must accept arbitrarily many arguments. The extra id and the expr just following it specify the contracts on the extra arguments. The value of id will alway be a list (of the extra arguments).

(case-> arrow-contract-expr ···)      CONTRACT-CASE->

The case-> expression constructs a contract for case-lambda function. It's arguments must all be function contracts, built by one of ->, ->d, ->*, or ->d*.

(opt-> (req-contracts ···) (opt-contracts ···) res-contract))      SYNTAX

(opt->* (req-contracts ···) (opt-contracts ···) (res-contracts ···))      SYNTAX

(opt->* (req-contracts ···) (opt-contracts ···) any)      SYNTAX

The opt-> expression constructs a contract for an opt-lambda function. The first arguments are the required parameters, the second arguments are the optional parameters and the final argument is the result. The req-contracts expressions, the opt-contracts expressions, and the res-contract expressions can be any expression that evaluates to a contract value.

Each opt-> expression expands into case->.

The opt->* expression constructs a contract for an opt-lambda function. The only difference between opt-> and opt->* is that multiple return values are permitted with opt->* and they are specified in the last clause of an opt->* expression. A result of any means any value or any number of values may be returned, and the contract does not inhibit tail-recursion.

13.3  Object and Class Contracts

This section describes contracts on classes and objects. Here is the basic shape of an object contract:

contract-expr ::== ···
 | (object-contract meth/field-spec ···)

meth/field-spec ::==
   (meth-name meth-contract)
 | (field field-name contract-expr)

meth-contract ::==
   (opt-> (required-contract-expr ···)
          (optional-contract-expr ···)
          any)
   (opt-> (required-contract-expr ···)
          (optional-contract-expr ···)
          result-contract-expr)
 | (opt->* (required-contract-expr ···)
           (optional-contract-expr ···)
           (result-contract-expr ···))
 | (case-> meth-arrow-contract ···)
 | meth-arrow-contract

meth-arrow-contract ::==
   (-> dom-contract-expr ··· rng-contract-expr)
 | (-> dom-contract-expr ··· (values rng-contract-expr ···))
 | (->* (dom-contract-expr ···) (rng-contract-expr ···))
 | (->* (dom-contract-expr ···) rest-arg-contract-expr (rng-contract-expr ···))
 | (->d dom-contract-expr ··· rng-contract-proc-expr)
 | (->d* (dom-contract-expr ···) rng-contract-proc-expr)
 | (->d* (dom-contract-expr ···) rest-contract-expr rng-contract-proc-expr)
 | (->r ((id expr) ···) expr)
 | (->r ((id expr) ···) id expr expr)

Each of the contracts for methods has the same semantics as the corresponding function contract (discussed above), 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).

mixin-contract      CONTRACT

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

(make-mixin-contract class-or-interface ···)      CONTRACT

make-mixin-contract is a function that constructs mixins contracts. It accepts any number of classes and interfaces and returns a function contract. The function contract guarantees that the input to the function implements the interfaces and is derived from the classes and that the result of the function is a subclass of the input.

13.4  Attaching Contracts to Values

There are three special forms that attach contract specification to values: provide/contract, define/contract, and contract.

(provide/contract p/c-item ···)      SYNTAX

p/c-item is one of
  (struct identifier ((identifier contract-expr) ···))
  (rename id id contract-expr)
  (id contract-expr)

A provide/contract form can only appear at the top-level of a module (see section 5 in PLT MzScheme: Language Manual). As with provide, each identifier is provided from the module. In addition, clients of the module must live up to the contract specified by expr.

The provide/contract form treats modules as units of blame. The module that defines the provided variable is expected to meet the (co-variant) positions of the contract. Each module that imports the provided variable must obey the negative (contra-variant) positions of the contract.

Only uses of the contracted variable outside the module are checked.

The rename form of a provide/contract exports the first variable (the internal name) with the name specified by the second variable (the external name).

The struct form of a provide/contract clause provides a structure definition. Each field has a contract that dictates the contents of the fields.

(define/contract id contract-expr init-value-expr)      SYNTAX

The define/contract form attaches the contract contract-expr to init-value-expr and binds that to id.

The define/contract form treats individual definitions as units of blame. The definition itself is responsible for positive (co-variant) positions of the contract and each reference to id (including those in the initial value expression) must meet the negative positions of the contract.

Error messages with define/contract are not as clear as those provided by provide/contract because define/contract cannot detect the name of the definition where the reference to the defined variable occurs. Instead, it uses the source location of the reference to the variable as the name of that definition.

(contract contract-expr to-protect-expr positive-blame negative-blame)      SYNTAX

(contract contract-expr to-protect-expr positive-blame negative-blame contract-source)      SYNTAX

The contract special form is the primitive mechanism for attaching a contract to a value. Its purpose is as a target for the expansion of some higher-level contract specifying form.

The contract form has this shape:

(contract expr to-protect-expr positive-blame negative-blame contract-source)

The contract expression adds the contract specified by the first argument to the value in the second argument. The result of a contract expression is the result of the to-protect-expr expression, but with the contract specified by contract-expr enforced on to-protect-expr. The expressions positive-blame and negative-blame must be symbols indicating how to assign blame for positive and negative positions of the contract specified by contract-expr. Finally, contract-source, if specified, indicates where the contract was assumed. It must be a syntax object specifying the source location of the location where the contract was assumed. If the syntax object wraps a symbol, the symbol is used as the name of the primitive whose contract was assumed. If absent, it defaults to the source location of the contract expression.

13.5  Contract Utility

contract?      PREDICATE

The procedure contract? returns #t if its argument is a contract (ie, constructed with one of the combinators described in this section).

flat-contract?      PREDICATE

This predicate returns true when its argument is a contract that has been constructed with flat-contract (and thus is essentially just a predicate).

(flat-contract-predicate value)      SELECTOR

This function extracts the predicate from a flat contract.