7.6 Attaching Contracts to Values

► |  | The Racket Reference

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

▼ | 7 | Contracts

7.1 | Data-structure Contracts
7.2 | Function Contracts
7.3 | Parametric Contracts
7.4 | Lazy Data-structure Contracts
7.5 | Structure Type Property Contracts
7.6 | Attaching Contracts to Values
7.7 | Building New Contract Combinators
7.8 | Contract Utilities
7.9 | racket/ contract/ base
7.10 | Legacy Contracts
7.11 | Random generation

► | 7.6 | Attaching Contracts to Values

7.6.1 | Nested Contract Boundaries
7.6.2 | Low-level Contract Boundaries

On this page:

contract-out
provide/ contract
7.6.1`` Nested Contract Boundaries
with-contract
define/ contract
define-struct/ contract
current-contract-region
7.6.2`` Low-level Contract Boundaries
contract

top ← prev up next →

#### 7.6`` Attaching Contracts to Values

> syntax (contract-out p/c-item ...)
> p/c-item |  | = |  | (struct id ((id contract-expr) ...))
> |  | | |  | (struct (id identifier) ((id contract-expr) ...))
> |  | | |  | (rename orig-id id contract-expr)
> |  | | |  | (id contract-expr)
> |  | | |  | #:∃ poly-variables
> |  | | |  | #:exists poly-variables
> |  | | |  | #:∀ poly-variables
> |  | | |  | #:forall poly-variables
> poly-variables |  | = |  | identifier
> |  | | |  | (identifier ...)

A provide-spec for use in provide (currently only for the same phase level as the provide form; for example, contract-out cannot be nested within for-syntax). Each id is provided from the module. In addition, clients of the module must live up to the contract specified by contract-expr for each export.

The contract-out form treats modules as units of blame. The module that defines the provided variable is expected to meet the positive (co-variant) positions of the contract. Each module that imports the provided variable must obey the negative (contra-variant) positions of the contract. Each contract-expr in a contract-out form is effectively moved to the end of the enclosing module, so a contract-expr can refer to variables that are defined later in the same module.

Only uses of the contracted variable outside the module are checked. Inside the module, no contract checking occurs.

The rename form of contract-out exports the first variable (the internal name) with the name specified by the second variable (the external name).

The struct form of contract-out provides a structure-type definition, and each field has a contract that dictates the contents of the fields. The structure-type definition must appear before the provide clause within the enclosing module. If the structure type has a parent, the second struct form (above) must be used, with the first name referring to the structure type to export and the second name referring to the parent structure type. Unlike a struct definition, however, all of the fields (and their contracts) must be listed. The contract on the fields that the sub-struct shares with its parent are only used in the contract for the sub-struct’s constructor, and the selector or mutators for the super-struct are not provided. The exported structure-type name always doubles as a constructor, even if the original structure-type name does not act as a constructor.

The #:∃, #:exists, #:∀, and #:forall clauses define new abstract contracts. The variables are bound in the remainder of the contract-out form to new contracts that hide the values they accept and ensure that the exported functions are treated parametrically. See new-∃/c and new-∀/c for details on how the clauses hide the values.

The implementation of contract-out attaches uses syntax-property to attach properties to the code it generates that records the syntax of the contracts in the fully expanded program. Specifically, the symbol 'provide/contract-original-contract is bound to vectors of two elements, the exported identifier and a syntax object for the expression that produces the contract controlling the export.

> syntax (provide/contract p/c-item ...)

A legacy shorthand for (provide (contract-out p/c-item ...)), except that a contract-expr within provide/contract is evaluated at the position of the provide/contract form instead of at the end of the enclosing module.

##### 7.6.1`` Nested Contract Boundaries

(require racket/contract/region)

> syntax (with-contract blame-id (wc-export ...) free-var-list ... body ...+)
> (with-contract blame-id results-spec free-var-list ... body ...+)
> wc-export |  | = |  | (id contract-expr)
> result-spec |  | = |  | #:result contract-expr
> |  | | |  | #:results (contract-expr ...)
> free-var-list |  | = |  |
> |  | | |  | #:freevar id contract-expr
> |  | | |  | #:freevars ([id contract-expr] ...)

Generates a local contract boundary.

The first with-contract form cannot appear in expression position. All names defined within the first with-contract form are visible externally, but those names listed in the wc-export list are protected with the corresponding contract. The body of the form allows definition/expression interleaving if its context does.

The second with-contract form must appear in expression position. The final body expression should return the same number of values as the number of contracts listed in the result-spec, and each returned value is contracted with its respective contract. The sequence of body forms is treated as for let.

The blame-id is used for the positive positions of contracts paired with exported ids. Contracts broken within the with-contract body will use the blame-id for their negative position.

If a free-var-list is given, then any uses of the free variables inside the body will be protected with contracts that blame the context of the with-contract form for the positive positions and the with-contract form for the negative ones.

> syntax (define/contract id contract-expr free-var-list init-value-expr)
> (define/contract (head args) contract-expr free-var-list body ...+)

Works like define, except that the contract contract-expr is attached to the bound value. For the definition of head and args, see define. For the definition of free-var-list, see with-contract.

The define/contract form treats the individual definition as a contract region. The definition itself is responsible for positive (co-variant) positions of the contract, and references to id outside of the definition must meet the negative positions of the contract. Since the contract boundary is between the definition and the surrounding context, references to id inside the define/contract form are not checked.

If a free-var-list is given, then any uses of the free variables inside the body will be protected with contracts that blame the context of the define/contract form for the positive positions and the define/contract form for the negative ones.

> > syntax
> > (define-struct/contract struct-id ([field contract-expr] ...)
> > struct-option ...)
> (define-struct/contract (struct-id super-struct-id)
> ([field contract-expr] ...)
> struct-option ...)

Works like define-struct, except that the arguments to the constructor, accessors, and mutators are protected by contracts. For the definitions of field and struct-option, see define-struct.

The define-struct/contract form only allows a subset of the struct-option keywords: #:mutable, #:transparent, #:auto-value, #:omit-define-syntaxes, #:property and #:omit-define-values.

Examples:
> > (define-struct/contract fish ([color number?]))
> > (make-fish 5)
> #<fish>
> > (make-fish #f)
> make-fish: contract violation
> expected: number?
> given: #f
> in: the 1st argument of
> (-> number? symbol? any)
> contract from: (struct fish)
> blaming: top-level
> > (define-struct/contract (salmon fish) ([ocean symbol?]))
> > (make-salmon 5 'atlantic)
> #<salmon>
> > (make-salmon 5 #f)
> make-salmon: contract violation
> expected: symbol?
> given: #f
> in: the 2nd argument of
> (-> any/c symbol? symbol? any)
> contract from: (struct salmon)
> blaming: top-level
> > (make-salmon #f 'pacific)
> make-fish: contract violation
> expected: number?
> given: #f
> in: the 1st argument of
> (-> number? symbol? any)
> contract from: (struct fish)
> blaming: top-level

> syntax current-contract-region

Bound by define-syntax-parameter, this contains information about the current contract region, used by the above forms to determine the candidates for blame assignment.

##### 7.6.2`` Low-level Contract Boundaries

> > syntax
> > (contract contract-expr to-protect-expr
> > positive-blame-expr negative-blame-expr)
> (contract contract-expr to-protect-expr
> positive-blame-expr negative-blame-expr
> value-name-expr source-location-expr)

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

The contract expression adds the contract specified by contract-expr to the value produced by to-protect-expr. 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 values of positive-blame-expr and negative-blame-expr indicate how to assign blame for positive and negative positions of the contract specified by contract-expr. They may be any value, and are formatted as by display for purposes of contract violation error messages.

If specified, value-name-expr indicates a name for the protected value to be used in error messages. If not supplied, or if value-name-expr produces #f, no name is printed. Otherwise, it is also formatted as by display.

If specified, source-location-expr indicates the source location reported by contract violations. The expression must produce a srcloc structure, syntax object, #f, or a list or vector in the format accepted by the third argument to datum->syntax.

top ← prev up next →