8.3 Parametric Contracts

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

On this page:

parametric->/ c
new-∀/ c
new-∃/ c

Racket
top contents ← prev up next →

### 8.3 Parametric Contracts🔗ℹ

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

The most convenient way to use parametric contract is to use contract-out’s #:∃, #:exists, #:∀, and #:forall keywords. The racket/contract/parametric library provides a few more, general-purpose parametric contracts.

> syntax (parametric->/c (x ...) c)

Creates a contract for parametric polymorphic functions. Each function is protected by c, where each x is bound in c and refers to a polymorphic type that is instantiated each time the function is applied.

At each application of a function, the parametric->/c contract constructs a new opaque wrapper for each x; values flowing into the polymorphic function (i.e. values protected by some x in negative position with respect to parametric->/c) are wrapped in the corresponding opaque wrapper. Values flowing out of the polymorphic function (i.e. values protected by some x in positive position with respect to parametric->/c) are checked for the appropriate wrapper. If they have it, they are unwrapped; if they do not, a contract violation is signaled.

Examples:

> > (define swap-ctc (parametric->/c [A B] (-> A B (values B A))))
> > (define/contract (good-swap a b)
> swap-ctc
> (values b a))
> > (good-swap 1 2)
> 2
> 1
> > (define/contract (bad-swap a b)
> swap-ctc
> (values a b))
> > (bad-swap 1 2)
> bad-swap: broke its own contract
> promised: B
> produced: #<A>
> in: the range of
> (parametric->/c (A B) (-> A B (values B A)))
> contract from: (function bad-swap)
> blaming: (function bad-swap)
> (assuming the contract is correct)
> at: eval:5:0
> > (define/contract (copy-first a b)
> swap-ctc
> (values a a))
> > (let ((v 'same-symbol)) (copy-first v v))
> copy-first: broke its own contract
> promised: B
> produced: #<A>
> in: the range of
> (parametric->/c (A B) (-> A B (values B A)))
> contract from: (function copy-first)
> blaming: (function copy-first)
> (assuming the contract is correct)
> at: eval:7:0
> > (define/contract (inspect-first a b)
> swap-ctc
> (if (integer? a)
> (+ a b)
> (raise-user-error "an opaque wrapped value is not an integer")))
> > (inspect-first 1 2)
> an opaque wrapped value is not an integer

> procedure (new-∀/c [name]) → contract?
> name : (or/c symbol? #f) = #f

Constructs a new universal contract.

Universal contracts accept all values when in negative positions (e.g., function inputs) and wrap them in an opaque struct, hiding the precise value. In positive positions (e.g. function returns), a universal contract accepts only values that were previously accepted in negative positions (by checking for the wrappers).

The name is used to identify the contract in error messages and defaults to a name based on the lexical context of new-∀/c.

For example, this contract:

> (let ([a (new-∀/c 'a)])
> (-> a a))

describes the identity function (or a non-terminating function). That is, the first use of the a appears in a negative position and thus inputs to that function are wrapped with an opaque struct. Then, when the function returns, it is checked to determine whether the result is wrapped, since the second a appears in a positive position.

The new-∀/c contract constructor is dual to new-∃/c.

> procedure (new-∃/c [name]) → contract?
> name : (or/c symbol? #f) = #f

Constructs a new existential contract.

Existential contracts accept all values when in positive positions (e.g., function returns) and wrap them in an opaque struct, hiding the precise value. In negative positions (e.g. function inputs), they accepts only values that were previously accepted in positive positions (by checking for the wrappers).

The name is used to identify the contract in error messages and defaults to a name based on the lexical context of new-∀/c.

For example, this contract:

> (let ([a (new-∃/c 'a)])
> (-> (-> a a)
> any/c))

describes a function that accepts the identity function (or a non-terminating function) and returns an arbitrary value. That is, the first use of the a appears in a positive position and thus inputs to that function are wrapped with an opaque struct. Then, when the function returns, it is checked to see if the result is wrapped, since the second a appears in a negative position.

The new-∃/c contract constructor is dual to new-∀/c.

top contents ← prev up next →