8.6 Attaching Contracts to Values

► |  | 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

▼ | 8 | Contracts

8.1 | Data-structure Contracts
8.2 | Function Contracts
8.3 | Parametric Contracts
8.4 | Lazy Data-structure Contracts
8.5 | Structure Type Property Contracts
8.6 | Attaching Contracts to Values
8.7 | Building New Contract Combinators
8.8 | Contract Utilities
8.9 | racket/ contract/ base
8.10 | Collapsible Contracts
8.11 | Legacy Contracts
8.12 | Random generation

► | 8.6 | Attaching Contracts to Values

8.6.1 | Nested Contract Boundaries
8.6.2 | Low-level Contract Boundaries

On this page:

contract-in
contract-out
recontract-out
provide/ contract
struct-guard/ c
8.6.1`` Nested Contract Boundaries
with-contract
define/ contract
struct/ contract
define-struct/ contract
invariant-assertion
current-contract-region
8.6.2`` Low-level Contract Boundaries
define-module-boundary-contract
contract

top contents ← prev up next →

#### 8.6`` Attaching Contracts to Values🔗ℹ

> syntax (contract-in module-path in-out-item ...)
> syntax (contract-out unprotected-submodule in-out-item ...)
> in-out-item |  | = |  | [id contract-expr]
> |  | | |  | (rename internal-id external-id contract-expr)
> (struct id/ignored ([id contract-expr] ...)
> struct-option)
> |  | | |  | #:∃ poly-variables
> |  | | |  | #:exists poly-variables
> |  | | |  | #:∀ poly-variables
> |  | | |  | #:forall poly-variables
> unprotected-submodule |  | = |  |
> |  | | |  | #:unprotected-submodule submodule-name
> poly-variables |  | = |  | id
> |  | | |  | (id ...)
> id/ignored |  | = |  | id
> |  | | |  | (id ignored-id)
> struct-option |  | = |  |
> |  | | |  | #:omit-constructor

Use contract-in in require and contract-out in provide (currently only for the same phase level as the provide form; for example, contract-out cannot be nested within for-syntax). Each identifier in contract-out is provided from the enclosing module and each one in contract-in is required from the named module. In addition, uses of the identifies must live up to the contract specified by contract-expr for each export.

The contract-out and contract-in forms treat modules as units of blame. The module that provides each identifier 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. Only uses of the contracted variable outside the module that provides them are checked. Inside the providing module, no contract checking occurs.

In a contract-out form, 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.

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

The struct form gives contracts to a structure-type definition id, and each field has a contract that dictates the contents of the fields. 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. If the #:omit-constructor option is present, the constructor is not provided. The second form of id/ignored, which has both id and ignored-id, is deprecated and allowed in the grammar only for backward compatibility, where ignored-id is ignored. The first form should be used instead.

Note that if the struct is created with serializable-struct or define-serializable-struct, contract-out does not protect struct instances that are created via deserialize. Consider using struct-guard/c instead.

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.

If #:unprotected-submodule appears, the identifier that follows it is used as the name of a submodule that contract-out generates. The submodule exports all of the names in the contract-out, but without contracts. In particular, the original structure-type name is exported for each struct form, which means #:omit-constructor only omits the extra constructor, if any.

The implementation of contract-out 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.

Examples:

> > (module math-example racket/base
> (require racket/contract)
> ; Compute the reciprocal of a real number
> (define (recip x) (/ 1 x))
> (provide
> (contract-out
> [recip (-> (and/c real? (not/c zero?)) real?)])))
> > (require 'math-example)
> > (recip 3)
> 1/3
> > (recip 1+2i)
> recip: contract violation
> expected: real?
> given: 1+2i
> in: an and/c case of
> the 1st argument of
> (-> (and/c real? (not/c zero?)) real?)
> contract from: math-example
> blaming: top-level
> (assuming the contract is correct)
> at: eval:2:0

Changed in version 7.3.0.3 of package base: Added #:unprotected-submodule.
Changed in version 7.7.0.9: Started ignoring ignored-id.
Changed in version 8.12.0.13: Added contract-in
Changed in version 8.13.0.1: Added rename and struct to contract-in

> syntax (recontract-out id ...)

A provide-spec for use in provide (currently, just like contract-out, only for the same phase level as the provide form).

It re-exports id, but with positive blame associated to the module containing recontract-out instead of the location of the original site of id.

This can be useful when a public module wants to export an identifier from a private module but where any contract violations should be reported in terms of the public module instead of the private one.

Examples:

> > (module private-implementation racket/base
> (require racket/contract)
> (define (recip x) (/ 1 x))
> (define (non-zero? x) (not (= x 0)))
> (provide/contract [recip (-> (and/c real? non-zero?)
> (between/c -1 1))]))
> > (module public racket/base
> (require racket/contract
> 'private-implementation)
> (provide (recontract-out recip)))
> > (require 'public)
> > (recip +nan.0)
> recip: broke its own contract
> promised: (between/c -1 1)
> produced: +nan.0
> in: the range of
> (->
> (and/c real? non-zero?)
> (between/c -1 1))
> contract from: public
> blaming: public
> (assuming the contract is correct)
> at: eval:3:0

Replacing the use of recontract-out with just recip would result in a contract violation blaming the private module.

> syntax (provide/contract unprotected-submodule in-out-item ...)

A legacy shorthand for (provide (contract-out unprotected-submodule in-out-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.

> syntax (struct-guard/c contract-expr ...)

Returns a procedure suitable to be passed as the #:guard argument to struct, serializable-struct (and related forms). The guard procedure ensures that each contract protects the corresponding field values, as long as the struct is not mutated. Mutations are not protected.

Examples:

> > (struct snake (weight hungry?)
> #:guard (struct-guard/c real? boolean?))
> > (snake 1.5 "yep")
> snake, field 2: contract violation
> expected: boolean?
> given: "yep"
> in: boolean?
> contract from: top-level
> blaming: top-level
> (assuming the contract is correct)
> at: eval:2:0

##### 8.6.1`` Nested Contract Boundaries🔗ℹ

(require racket/contract/region) | package: base

> 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.

Examples:

> > (define/contract distance (>=/c 0) 43.52)
> > (define/contract (furlongs->feet fr)
> (-> real? real?)
> (* 660 fr))
> ; a contract violation expected here:
> > (furlongs->feet "not a furlong")
> furlongs->feet: contract violation
> expected: real?
> given: "not a furlong"
> in: the 1st argument of
> (-> real? real?)
> contract from: (function furlongs->feet)
> blaming: top-level
> (assuming the contract is correct)
> at: eval:3:0

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.

Examples:

> ; an unsual predicate that prints when called
> > (define (printing-int? x)
> (displayln "I was called")
> (exact-integer? x))
> > (define/contract (fact n)
> (-> printing-int? printing-int?)
> (if (zero? n)
> 1
> (* n (fact (sub1 n)))))
> > (fact 5) ; only prints twice, not for each recursive call
> I was called
> I was called
> 120

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.

Examples:

> > (define (integer->binary-string n)
> (number->string n 2))
> > (define/contract (numbers->strings lst)
> (-> (listof number?) (listof string?))
> #:freevar integer->binary-string (-> exact-integer? string?)
> ; mistake, lst might contain inexact numbers
> (map integer->binary-string lst))
> > (numbers->strings '(4.0 3.3 5.8))
> integer->binary-string: contract violation
> expected: exact-integer?
> given: 4.0
> in: the 1st argument of
> (-> exact-integer? string?)
> contract from: top-level
> blaming: (function numbers->strings)
> (assuming the contract is correct)
> at: eval:3:0

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

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

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

Examples:

> > (struct/contract fruit ([seeds number?]))
> > (fruit 60)
> #<fruit>
> > (fruit #f)
> fruit: contract violation
> expected: number?
> given: #f
> in: the 1st argument of
> (-> number? symbol? any)
> contract from: (struct fruit)
> blaming: top-level
> (assuming the contract is correct)
> > (struct/contract apple fruit ([type string?]))
> > (apple 14 "golden delicious")
> #<apple>
> > (apple 5 30)
> apple: contract violation
> expected: string?
> given: 30
> in: the 2nd argument of
> (-> any/c string? symbol? any)
> contract from: (struct apple)
> blaming: top-level
> (assuming the contract is correct)
> > (apple #f "granny smith")
> fruit: contract violation
> expected: number?
> given: #f
> in: the 1st argument of
> (-> number? symbol? any)
> contract from: (struct fruit)
> blaming: top-level
> (assuming the contract is correct)

> > 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 struct/contract, except that the syntax for supplying a super-struct-id is different, and a constructor-id that has a make- prefix on struct-id is implicitly supplied. For the definitions of field and struct-option, see define-struct. Like struct versus define-struct, struct/contract is normally preferred to define-struct/contract.

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

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
> (assuming the contract is correct)
> > (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
> (assuming the contract is correct)
> > (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
> (assuming the contract is correct)

> syntax (invariant-assertion invariant-expr expr)

Establishes an invariant of expr, determined by invariant-expr.

Unlike the specification of a contract, an invariant-assertion does not establish a boundary between two parties. Instead, it simply attaches a logical assertion to the value. Because the form uses contract machinery to check the assertion, the surrounding module is treated as the party to be blamed for any violations of the assertion.

This means, for example, that the assertion is checked on recursive calls, when an invariant is used on the right-hand side of a definition:

Examples:

> > (define furlongss->feets
> (invariant-assertion
> (-> (listof real?) (listof real?))
> (λ (l)
> (cond
> [(empty? l) empty]
> [else
> (if (= 327 (car l))
> (furlongss->feets (list "wha?"))
> (cons (furlongs->feet (first l))
> (furlongss->feets (rest l))))]))))
> > (furlongss->feets (list 1 2 3))
> '(660 1320 1980)
> > (furlongss->feets (list 1 327 3))
> furlongss->feets: assertion violation
> expected: real?
> given: "wha?"
> in: an element of
> the 1st argument of
> (-> (listof real?) (listof real?))
> contract from: invariant-assertion
> at: eval:5:0

Added in version 6.0.1.11 of package base.

> 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.

##### 8.6.2`` Low-level Contract Boundaries🔗ℹ

> > syntax
> > (define-module-boundary-contract id
> > orig-id
> > contract-expr
> > d-m-b-c-kwd-arg ...)
> d-m-b-c-kwd-arg |  | = |  | #:name-for-contract name-for-contract-id
> |  | | |  | #:name-for-blame blame-id
> |  | | |  | #:srcloc srcloc-expr
> |  | | |  | #:pos-source pos-source-expr
> |  | | |  | #:context-limit limit-expr
> |  | | |  | #:lift-to-end? boolean
> |  | | |  | #:start-swapped? boolean

Defines id to be orig-id, but with the contract contract-expr.

The identifier id is defined as a macro transformer that consults the context of its use to determine the name for negative blame assignment (using the entire module where a reference appears as the negative party).

The name used in the error messages will be orig-id, unless #:name-for-blame is supplied, in which case the identifier following it is used as the name in the error messages.

The contract expression is wrapped in a let to give it a name which will be passed on to the name of the wrapped value in certain situations (e.g., if the contract is a function contract). If name-for-contract-id is supplied, the identifier that follows it is used to name the contract; otherwise orig-id is used.

The source location used in the blame error messages for the location of the place where the contract was put on the value defaults to the source location of the use of define-module-boundary-contract, but can be specified via the #:srcloc argument, in which case it can be any of the things that the third argument to datum->syntax can be.

The positive party defaults to the module containing the use of define-module-boundary-contract, but can be specified explicitly via the #:pos-source keyword.

If #:context-limit is supplied, it behaves the same as it does when supplied to contract.

If lift-to-end? is #t or is not supplied, then the contract expression is placed at the end of the enclosing module (using syntax-local-lift-module-end-declaration). If it is supplied and #f, the contract expression is placed where define-module-boundary-contract is placed.

If start-swapped? is #t, then the initial blame object is created in the “swapped?” state, and the pos-source is used as a negative source. This is helpful to get the “contract from:” line in contract violations correct in certain situations. If #:start-swapped? is not supplied, it is treated as if it was supplied as #f.

Examples:

> > (module server racket/base
> (require racket/contract/base)
> (define (f x) #f)
> (define-module-boundary-contract g f (-> integer? integer?))
> (provide g))
> > (module client racket/base
> (require 'server)
> (define (clients-fault) (g #f))
> (define (servers-fault) (g 1))
> (provide servers-fault clients-fault))
> > (require 'client)
> > (clients-fault)
> g: contract violation
> expected: integer?
> given: #f
> in: the 1st argument of
> (-> integer? integer?)
> contract from: 'server
> blaming: client
> (assuming the contract is correct)
> at: eval:2:0
> > (servers-fault)
> g: broke its own contract
> promised: integer?
> produced: #f
> in: the range of
> (-> integer? integer?)
> contract from: 'server
> blaming: (quote server)
> (assuming the contract is correct)
> at: eval:2:0

Changed in version 6.7.0.4 of package base: Added the #:name-for-blame argument.
Changed in version 6.90.0.29: Added the #:context-limit argument.
Changed in version 8.13.0.1: Added the #:name-for-contract and #:start-swapped arguments.

> > 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
> #:context-limit limit-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. More precisely, the value-name-expr ends up in the blame-value field of the blame record, which is used as the first portion of the error message.

Examples:

> > (contract integer? #f 'pos 'neg 'timothy #f)
> timothy: broke its own contract
> promised: integer?
> produced: #f
> in: integer?
> contract from: pos
> blaming: pos
> (assuming the contract is correct)
> > (contract integer? #f 'pos 'neg #f #f)
> broke its own contract
> promised: integer?
> produced: #f
> in: integer?
> contract from: pos
> blaming: pos
> (assuming the contract is correct)

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.

If #:context-limit is supplied, the following expression must evaluate to either #f or a natural number. If the expression evaluates to an natural number, the number of layers of context information is limited to at most that many. For example, if the number is 0, no context information is recorded and the error messages do not contain the section that starts with in:.

top contents ← prev up next →