8.5 Structure Type Property Contracts (original) (raw)

8.14

8.5 Structure Type Property Contracts🔗

Produces a contract for a structure type property. When the contract is applied to a struct type property, it produces a wrapped struct type property that applies value-contract to the value associated with the property when it used to create a new struct type (via struct, make-struct-type, etc).

The struct type property’s accessor function is not affected; if it is exported, it must be protected separately.

As an example, consider the following module. It creates a structure type property, prop, whose value should be a function mapping a structure instance to a numeric predicate. The module also exportsapp-prop, which extracts the predicate from a structure instance and applies it to a given value.

The structmod module creates a structure type nameds with a single field; the value of prop is a function that extracts the field value from an instance. Thus the field ought to be an integer predicate, but notice thatstructmod places no contract on s enforcing that constraint.

First we create an s instance with an integer predicate, so the constraint on prop is in fact satisfied. The first call to app-prop is correct; the second simply violates the contract of app-prop.

> (define s1 (s even?))
> (app-prop s1 5)
#f
> (app-prop s1 'apple)
app-prop: contract violation
expected: integer?
given: 'apple
in: the 2nd argument of
(-> prop? integer? boolean?)
contract from: propmod
blaming: top-level
(assuming the contract is correct)
at: eval:2:0

We are able to create s instances with values other than integer predicates, but applying app-prop on them blamesstructmod, because the function associated withprop—that is, (lambda (s) (s-f s))—does not always produce a value satisfying (-> integer? boolean?).

> (define s2 (s "not a fun"))
> (app-prop s2 5)
prop: contract violation
expected: a procedure
given: "not a fun"
in: the range of
the struct property value of
(struct-type-property/c
(-> prop? (-> integer? boolean?)))
contract from: propmod
blaming: structmod
(assuming the contract is correct)
at: eval:2:0
> (define s3 (s list))
> (app-prop s3 5)
prop: contract violation
expected: boolean?
given: '(5)
in: the range of
the range of
the struct property value of
(struct-type-property/c
(-> prop? (-> integer? boolean?)))
contract from: propmod
blaming: structmod
(assuming the contract is correct)
at: eval:2:0

The fix would be to propagate the obligation inherited fromprop to s:

Finally, if we directly apply the property accessor,prop-ref, and then misuse the resulting function, thepropmod module is blamed:

> ((prop-ref s3) 'apple)
prop: broke its own contract
promised: prop?
produced: 'apple
in: the 1st argument of
the struct property value of
(struct-type-property/c
(-> prop? (-> integer? boolean?)))
contract from: propmod
blaming: propmod
(assuming the contract is correct)
at: eval:2:0

The propmod module has an obligation to ensure a function associated with prop is applied only to values satisfyingprop?. By directly providing prop-ref, it enables that constraint to be violated (and thus it is blamed), even though the bad application actually occurs elsewhere.

Generally there is no need to provide a structure type property accessor at all; it is typically only used by other functions within the module. But if it must be provided, it should be protected thus: