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: