On this page:
top
up

7.1Contracts and BoundariesπŸ”— i

Like a contract between two business partners, a software contract is an agreement between two parties. The agreement specifies obligations and guarantees for each “product” (or value) that is handed from one party to the other.

A contract thus establishes a boundary between the two parties. Whenever a value crosses this boundary, the contract monitoring system performs contract checks, making sure the partners abide by the established contract.

In this spirit, Racket encourages contracts mainly at module boundaries. Specifically, programmers may attach contracts to provide clauses and thus impose constraints and promises on the use of exported values. For example, the export specification
(define amount... )

promises to all clients of the above module that the value of amount will always be a positive number. The contract system monitors the module’s obligation carefully. Every time a client refers to amount, the monitor checks that the value of amount is indeed a positive number.

The contracts library is built into the Racket language, but if you wish to use racket/base, you can explicitly require the contracts library like this:

(require racket/contract);now we can write contracts
(define amount... )

7.1.1Contract ViolationsπŸ”— i

If we bind amount to a number that is not positive,

(define amount0)

then, when the module is required, the monitoring system signals a violation of the contract and blames the module for breaking its promises.

An even bigger mistake would be to bind amount to a non-number value:

(define amount'amount)

In this case, the monitoring system will apply positive? to a symbol, but positive? reports an error, because its domain is only numbers. To make the contract capture our intentions for all Racket values, we can ensure that the value is both a number and is positive, combining the two contracts with and/c :

(provide (contract-out [amount(and/c number? positive? )]))

7.1.2Experimenting with Contracts and ModulesπŸ”— i

All of the contracts and modules in this chapter (excluding those just following) are written using the standard #lang syntax for describing modules. Since modules serve as the boundary between parties in a contract, examples involve multiple modules.

To experiment with multiple modules within a single module or within DrRacket’s definitions area, use Racket’s submodules. For example, try the example earlier in this section like this:

(module+ server
(define amount150))
(module+ main
(require (submod ".."server))
(+ amount10))

Each of the modules and their contracts are wrapped in parentheses with the module+ keyword at the front. The first form after module is the name of the module to be used in a subsequent require statement (where each reference through a require prefixes the name with "..").

7.1.3Experimenting with Nested Contract BoundariesπŸ”— i

In many cases, it makes sense to attach contracts at module boundaries. It is often convenient, however, to be able to use contracts at a finer granularity than modules. The define/contract form enables this kind of use:

150)
(+ amount10)

In this example, the define/contract form establishes a contract boundary between the definition of amount and its surrounding context. In other words, the two parties here are the definition and the module that contains it.

Forms that create these nested contract boundaries can sometimes be subtle to use because they may have unexpected performance implications or blame a party that may seem unintuitive. These subtleties are explained in Using define/contract and -> and Contract boundaries and define/contract.

top
up

AltStyle γ«γ‚ˆγ£γ¦ε€‰ζ›γ•γ‚ŒγŸγƒšγƒΌγ‚Έ (->γ‚ͺγƒͺγ‚ΈγƒŠγƒ«) /