On this page:
struct-type-property/  c
5.92

8.5 Structure Type Property Contracts

procedure

(struct-type-property/c value-contract)  contract?

  value-contract : contract?
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 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

  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 propthat 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 that accepts 1 mandatory argument

without any keywords

  given: "not a fun"

  in: the range of

      ...

      (struct-type-property/c

       (-> prop? (-> integer? boolean?)))

  contract from: propmod

  blaming: structmod

  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

      ...

      (struct-type-property/c

       (-> prop? (-> integer? boolean?)))

  contract from: propmod

  blaming: structmod

  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

      ...

      (struct-type-property/c

       (-> prop? (-> integer? boolean?)))

  contract from: propmod

  blaming: propmod

  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?)))]))