8.5 Structure Type Property Contracts
procedure
(struct-type-property/c value-contract) → contract?
value-contract : contract?
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 exports app-prop, which extracts the predicate from a structure instance and applies it to a given value.
> (module propmod racket (require racket/contract) (define-values (prop prop? prop-ref) (make-struct-type-property 'prop)) (define (app-prop x v) (((prop-ref x) x) v)) (provide/contract [prop? (-> any/c boolean?)] [prop (struct-type-property/c (-> prop? (-> integer? boolean?)))] [app-prop (-> prop? integer? boolean?)]) (provide prop-ref))
The structmod module creates a structure type named s 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 that structmod places no contract on s enforcing that constraint.
> (module structmod racket (require 'propmod) (struct s (f) #:property prop (lambda (s) (s-f s))) (provide (struct-out s)))
> (require 'propmod 'structmod)
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 blames
structmod, because the function associated with
prop—
> (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 from prop to s:
(provide (contract-out [struct s ([f (-> integer? boolean?)])]))
Finally, if we directly apply the property accessor, prop-ref, and then misuse the resulting function, the propmod module is blamed:
> ((prop-ref s3) 'apple) prop: broke its 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 satisfying prop?. 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:
(provide (contract-out [prop-ref (-> prop? (-> prop? (-> integer? boolean?)))]))