[racket-dev] `make-struct-type-property` and impersonators

From: Asumu Takikawa (asumu at ccs.neu.edu)
Date: Tue Jul 17 17:01:50 EDT 2012

Hi all,

I mentioned an issue I had with struct type properties & impersonators
in the promise/c thread, but I figured I should explain this in more
detail.

Currently, `make-struct-type-property` takes an optional argument which
is the "guard" for the property. This guard is a procedure that checks
the value coming from *users* of a property (via the `struct` form
and #:property keyword). This is useful so that the implementor of a
struct type property can rely on this guard invariant for whatever
internal processing is needed.

However, the guard can also be the 'can-impersonate symbol. In this
case, there is no guard procedure and the struct type property accessor
(the procedure actually used to obtain the stored value) can be
impersonated.

i.e., only one of these two are allowed:

    (define-values (prop:widget widget? prop:widget-accessor)
      (make-struct-type-property 'widget valid-widget?))

    (define-values (prop:widget widget? prop:widget-accessor)
      (make-struct-type-property 'widget 'can-impersonate))

but not both.

The issue is that there is a use case for having *both* impersonation of
the accessor and a guard. The reason is that there are two interactions
involved in a property: one interaction between the property implementor
and the struct type creator, who provides the initial value using the
`struct` form, and another interaction involving the client who accesses
the property value in an existing structure instance.

e.g.,
  (struct a-widget (...)
    #:property prop:widget some-widget) ; guarded by `valid-widget?`

  vs.

  (prop:widget-accessor w) ; not guarded, could be redirected
                           ; by some impersonator


The guard protects the property implementor in the former interaction,
whereas the client is the one that is affected by any impersonation of
the property. Since these two interactions aren't necessarily related,
it's still useful to guard the property even if the property can be
arbitrarily impersonated.

One example where I needed this is to attach impersonator contracts
(i.e., polymorphic contracts) to a method table in the generics
implementation. The table is stored in a struct property and needs to be
impersonatable for contracting.

However, I don't want to introduce additional code that duplicates the
sanity checking that the guard would guarantee for the initial methods
provided by the implementor of an *instance* of a generic interface.

   ***

That was my summary of the problem. I propose to change the API a bit to
make it more flexible: `make-struct-type-property` would take a keyword
#:can-impersonate which will control impersonation entirely orthogonal
to the guard argument (which would now be either #f or a procedure).

I think this API would cover more of the possible use cases than what is
currently available.

One way to summarize the use of the guard is that with
non-impersonatable properties, it is making a guarantee about what it is
in the property. With impersonatable properties, it can only make a
guarantee about the *initial* value of the property. The latter can
still be useful though.

Any thoughts?

Cheers,
Asumu

Posted on the dev mailing list.