posted by Stevie
In SVN I’ve added three new major features that involve contracts. One allows for more fine-grained control of contracts, and the other two allow for the use of contracts with signatures and units.
Contract regions allow the programmer to protect a region of code with a contract boundary. In addition to the wrapped code, the programmer also provides a name for the region which is used in blame situations and a list of exported variables which can either be protected with contracts or unprotected. The region provides a true contract boundary, in that uses of contracted exports within the region are unprotected. Contract regions are specified with the
with-contract form. The following contract region defines two mutually recursive functions:
Notice that the blame not only gives the name of the region, but describes what type of contract boundary was involved.
For contracting a single definition, there is the
define/contract form which has a similar syntax to define, except that it takes a contract before the body of the definition.
To compare the two forms, the following two definitions are equivalent:
First order checks are similarly performed at the definition for
(function fact) broke the contract (-> number?) on fact; expected a procedure that accepts no arguments without any keywords, given: #<procedure:fact>
In addition to contract regions, units are also now contract boundaries. One way to use contracts with units is to add contracts to unit signatures via the contracted
Notice that contracts in a signature can use variables listed in the signature.Now if we take the following implementation of that signature:
We get the appropriate contract checks on those exports:
As before, uses of contracted exports inside the unit are not checked.
Since units are contract boundaries, they can be blamed appropriately. Take the following definitions:
However, we may not always be able to add contracts to signatures. For example, there are many already-existing signatures in PLT Scheme that one may want to implement, or a programmer may want to take a unit value and add contracts to it after the fact.
To do this, there is the
unit/c contract combinator. It takes a list of imports and exports, where each signature is paired with a list of variables and their contracts for each signature. So if we had the uncontracted version of the toy-factory^ signature:
the following contracts would be appropriate for a unit that imports nothing and exports that signature:
Unit contracts can contain a superset of the import signatures and a subset of the export signatures for a given unit value. Also, variables that are not listed for a given signature are left alone when the contracts are being added.
Since the results of applying
unit/c is a new unit, then adding a contract can cause link inference to fail. For example, if we change the definition of
[email protected] above to
Then when we try to combine it with the
[email protected] unit, we get:
One way to solve this is to use
define-unit-binding to set up the static information for the new contracted value. Another possibility for unit definitions is to use
More about these features can be found in the Reference, and a short section about signature and unit contracts has been added to the Guide.