13.5 Chaperones
A chaperone is a wrapper for a value where the wrapper implements primitive support for contract-like checks on the value’s operations. Chaperones apply only to procedures, structures for which an accessor or mutator is available, structure types, hash tables, vectors, boxes. A chaperoned value is equal? to the original value, but not eq? to the original value.
A chaperone’s refinement of a value’s operation is restricted to side effects (including, in particular, raising and exception) or chaperoning values supplied to or produced by the operation. For example, a vector chaperone can redirect vector-ref to raise an exception if the accessed vector slot contains a string, or it can cause the result of vector-ref to be a chaperoned variant of the value that is in the accessed vector slot, but it cannot redirect vector-ref to produce a value that is arbitrarily different from the value in the vector slot.
Beware that each of the following operations can be redirected to arbitrary procedure through chaperones on the operation’s argument – assuming that the operation is available to the creator of the chaperone:
a structure-field accesor
a structure-field mutator
a structure type property accessor
application of a procedure
Derived operations, such as printing a value, can be redirected through chaperones due to their use of accessor functions. The equal?, equal-hash-code, and equal-secondary-hash-code operations, in contrast, may bypass chaperones (but they are not obliged to).
In addition to redirecting operations that work on a value, a chaperone can include chaperone properties for a chaperoned value. A chaperone property is similar to a structure type property, but it applies to chaperones instead of structure types and their instances.
(chaperone? v) → boolean? |
v : any/c |
Programs and libraries generally should avoid chaperone? and treat chaperones the same as unchaperoned values. In rare cases, chaperone? may be needed to guard against redirection by a chaperone of an operation to an arbitrary procedure.
(chaperone-of? v1 v2) → boolean? |
v1 : any/c |
v2 : any/c |
For values that include no chaperones, v1 and v2 can be considered chaperones of each other if they are equal?, except that the mutability of vectors and boxes with v1 and v2 must be the same.
Otherwise, all chaperones of v2 must be intact in v1, in the sense that parts of v2 must be derived from v1 through one of the chaperone constructors (e.g., chaperone-procedure).
13.5.1 Chaperone Constructors
| ||||||||||||||||||||
→ (and/c procedure? chaperone?) | ||||||||||||||||||||
proc : procedure? | ||||||||||||||||||||
wrapper-proc : procedure? | ||||||||||||||||||||
prop : chaperone-property? | ||||||||||||||||||||
prop-val : any |
The arity of wrapper-proc must include the arity of proc. The allowed keyword arguments of wrapper-proc must be a superset of the allowed keywords of proc. The required keyword arguments of wrapper-proc must be a subset of the required keywords of proc.
For applications without keywords, the result of wrapper-proc must be either the same number of values as supplied to it or one more than the number of supplied values, where an extra result is supplied before the others. For each supplied value, the corresponding result must be the same or a chaperone of (in the sense of chaperone-of?) the supplied value. The additional result, if any, that precedes the chaperoned values must be a procedure that accepts as many results as produced by proc; it must return the same number of results, each of which is the same or a chaperone of the corresponding original result. If wrapper-proc returns the same number of values as it is given (i.e., it does not return a procedure to chaperone proc’s result), then proc is called in tail position with respect to the call to the chaperone.
For applications that include keyword arguments, wrapper-proc must return an additional value before any other values but after the result-chaperoning procedure (if any). The additional value must be a list of chaperones of the keyword arguments that were supplied to the chaperoned procedure (i.e., not counting optional arguments that were not supplied). The arguments must be ordered according to the sorted order of the supplied arguments’ keywords.
Pairs of prop and prop-val (the number of arguments to procedure-chaperone must be even) add chaperone properties or override chaperone-property values of proc.
| |||||||||||||||||||||||||||||||||||||||||||||||||
v : any/c | |||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||
redirect-proc : procedure? | |||||||||||||||||||||||||||||||||||||||||||||||||
prop : chaperone-property? | |||||||||||||||||||||||||||||||||||||||||||||||||
val : any |
The protocol for a redirect-proc depends on the corresponding orig-proc:
A structure-field or property accessor: orig-proc must accept two arguments, v and the value field-v that orig-proc produces for v; it must return chaperone of field-v.
A structure field mutator: orig-proc must accept two arguments, v and the value field-v supplied to the mutator; it must return chaperone of field-v to be propagated to orig-proc and v.
struct-info: orig-proc must accept two values, which are the results of struct-info on v; it must return two values that are chaperones of its arguments. The orig-proc is not called if struct-info would return #f as its first argument.
An orig-proc can be struct-info only if some other orig-proc is supplied, and each orig-proc must indicate a distinct operation. If no orig-procs are supplied, then no props must be supplied, and v is returned unchaperoned.
Pairs of prop-val and val (the number of arguments to chaperone-procedure must be even) add chaperone properties or override chaperone-property values of v.
| ||||||||||||||||||||||||||||||||||||||||||
vec : vector? | ||||||||||||||||||||||||||||||||||||||||||
ref-proc : (vector? exact-nonnegative-integer? any/c . -> . any/c) | ||||||||||||||||||||||||||||||||||||||||||
set-proc : (vector? exact-nonnegative-integer? any/c . -> . any/c) | ||||||||||||||||||||||||||||||||||||||||||
prop : chaperone-property? | ||||||||||||||||||||||||||||||||||||||||||
val : any |
The ref-proc must accept vec, an index passed to vector-ref, and the value that vector-ref on vec produces for the given index; it must produce the same value or a chaperone of the value, which is the result of vector-ref on the chaperone.
The set-proc must accept vec, an index passed to vector-set!, and the value passed to vector-set!; it must produce the same value or a chaperone of the value, which is used with vector-set! on the original vec to install the value. The set-proc will not be used if vec is immutable.
Pairs of prop-val and val (the number of arguments to chaperone-vector must be odd) add chaperone properties or override chaperone-property values of vec.
| ||||||||||||||||||||||||||||||||||||||||||
bx : box? | ||||||||||||||||||||||||||||||||||||||||||
unbox-proc : (box? any/c . -> . any/c) | ||||||||||||||||||||||||||||||||||||||||||
set-proc : (box? any/c . -> . any/c) | ||||||||||||||||||||||||||||||||||||||||||
prop : chaperone-property? | ||||||||||||||||||||||||||||||||||||||||||
val : any |
The unbox-proc must accept bx and the value that unbox on bx produces index; it must produce the same value or a chaperone of the value, which is the result of unbox on the chaperone.
The set-proc must accept bx and the value passed to set-box!; it must produce the same value or a chaperone of the value, which is used with set-box! on the original bx to install the value. The set-proc will not be used if bx is immutable.
Pairs of prop-val and val (the number of arguments to chaperone-box must be odd) add chaperone properties or override chaperone-property values of bx.
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
hash : hash? | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
set-proc : (hash? any/c any/c . -> . (values any/c any/c)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
remove-proc : (hash? any/c . -> . any/c) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
key-proc : (hash? any/c . -> . any/c) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
prop : chaperone-property? | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
val : any |
The ref-proc must accept hash and a key passed hash-ref. It must returned the key or a chaperone of the key as well as a procedure. The returned procedure is called only if the returned key is found in hash via hash-ref, in which case the procedure is called with hash, the previously returned key, and the found value. The returned procedure must itself return the found value or a chaperone of the value.
The set-proc must accept hash, a key passed to hash-set! or hash-set, and the value passed to hash-set! or hash-set; it must produce two values: the same key or a chaperone of the key and the same value or a chaperone of the value. The returned key and value are used with hash-set! or hash-set on the original hash to install the value.
The remove-proc must accept hash and a key passed to hash-remove! or hash-remove; it must produce the same key or a chaperone of the key, which is used with hash-remove! or hash-remove on the original hash to remove any mapping using the (chaperoned) key.
The key-proc must accept hash and a key that has been extracted from hash (by hash-iterate-key or other operations that use hash-iterate-key internally); it must produce the same key or a chaperone of the key, which is then reported as a key extracted from the table.
Pairs of prop-val and val (the number of arguments to chaperone-hash must be odd) add chaperone properties or override chaperone-property values of hash.
| ||||||||||||||||||||||||||||
→ (and/c struct-type? chaperone?) | ||||||||||||||||||||||||||||
struct-type : struct-type? | ||||||||||||||||||||||||||||
struct-info-proc : procedure? | ||||||||||||||||||||||||||||
make-constructor-proc : (procedure? . -> . procedure?) | ||||||||||||||||||||||||||||
guard-proc : procedure? | ||||||||||||||||||||||||||||
prop : chaperone-property? | ||||||||||||||||||||||||||||
val : any |
The struct-info-proc must accept 8 arguments – the result of struct-type-info on struct-type. It must return 8 values, where each is the same or a chaperone of the corresponding argument. The 8 values are used as the results of struct-type-info for the chaperoned structure type.
The make-constructor-proc must accept a single procedure argument, which is a constructor produced by struct-type-make-constructor on struct-type. It must return the same or a chaperone of the procedure, which is used as the result of struct-type-make-constructor on the chaperoned structure type.
The guard-proc must accept as many argument as a constructor for struct-type; it must return the same number of arguments, each the same or a chaperone of the corresponding argument. The guard-proc is added as a constructor guard when a subtype is created of the chaperoned structure type.
Pairs of prop-val and val (the number of arguments to chaperone-struct-type must be even) add chaperone properties or override chaperone-property values of struct-type.
13.5.2 Chaperone Properties
(make-chaperone-property name) | |||||||||
| |||||||||
name : symbol? |
a chaperone property descriptor, for use with chaperone-procedure, chaperone-struct, and other chaperone constructors;
a chaperone property predicate procedure, which takes an arbitrary value and returns #t if the value is a chaperone with a value for the property, #f otherwise;
an chaperone property accessor procedure, which returns the value associated with a chaperone for the property; if a value given to the accessor is not a chaperone or does not have a value for the property, the exn:fail:contract exception is raised.
(chaperone-property? v) → boolean? |
v : any/c |