7.3Parametric Contracts

 (require racket/contract/parametric)

The most convenient way to use parametric contract is to use contract-out’s #:exists keyword. The racket/contract/parametric 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/contract (check x y) (parametric->/c [X] (boolean? X . -> . X)) (if (or (not x) (equal? y 'surprise)) 'invalid y))
> (check #t 'ok)

'ok

> (check #f 'ignored)

check: broke its contract

promised: X

produced: 'invalid

in: the range of

...

(parametric->/c (X) ...)

contract from: (function check)

blaming: (function check)

at: eval:2.0

> (check #t 'surprise)

'surprise

 procedure(new-∀/c name) → contract? name : symbol?
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.

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 construct constructor is dual to new-∃/c.

 procedure(new-∃/c name) → contract? name : symbol?
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.

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 construct constructor is dual to new-∀/c.