racket/pkgs/racket-doc/scribblings/reference/chaperones.scrbl
Matthew Flatt fe1d9ee517 document/test optional argument of impersonator propertry accessor
An impersonator-property accessor accepts a failure argument
in the same was as a structure-type--property accessor.
2017-12-22 19:07:36 -07:00

1122 lines
50 KiB
Racket

#lang scribble/doc
@(require "mz.rkt")
@(define-syntax op
(syntax-rules ()
[(_ (x ...)) (x ...)]
[(_ id) @racket[id]]))
@(define-syntax-rule (operations i ...)
(itemlist #:style 'compact @item{@op[i]} ...))
@title[#:tag "chaperones"]{Impersonators and Chaperones}
An @deftech{impersonator} is a wrapper for a value where the wrapper
redirects some of the value's operations. Impersonators apply only to procedures,
@tech{structures} for which an accessor or mutator is available,
@tech{structure types}, @tech{hash tables}, @tech{vectors},
@tech{box}es, @tech{channels}, and @tech{prompt tag}s.
An impersonator is @racket[equal?] to the original
value, but not @racket[eq?] to the original value.
A @deftech{chaperone} is a kind of impersonator whose refinement of a value's
operation is restricted to side effects (including, in particular,
raising an exception) or chaperoning values supplied to or produced by
the operation. For example, a vector chaperone can redirect
@racket[vector-ref] to raise an exception if the accessed vector slot
contains a string, or it can cause the result of @racket[vector-ref]
to be a chaperoned variant of the value that is in the accessed vector
slot, but it cannot redirect @racket[vector-ref] to produce a value
that is arbitrarily different from the value in the vector slot.
A non-@tech{chaperone} @tech{impersonator}, in contrast, can refine an operation to swap one
value for any other. An impersonator cannot be applied to an immutable value
or refine the access to an immutable field in an instance of a @tech{structure
type}, since arbitrary redirection of an operation amounts to
mutation of the impersonated value.
Beware that each of the following operations can be redirected to an
arbitrary procedure through an impersonator on the operation's
argument---assuming that the operation is available to the creator of
the impersonator:
@operations[@t{a structure-field accessor}
@t{a structure-field mutator}
@t{a structure type property accessor}
@t{application of a procedure}
unbox set-box!
vector-ref vector-set!
hash-ref hash-set hash-set! hash-remove hash-remove!
channel-get channel-put
call-with-continuation-prompt
abort-current-continuation]
Derived operations, such as printing a value, can be redirected
through impersonators due to their use of accessor functions. The
@racket[equal?], @racket[equal-hash-code], and
@racket[equal-secondary-hash-code] operations, in contrast, may bypass
impersonators (but they are not obliged to).
In addition to redirecting operations that work on a value, a
impersonator can include @deftech{impersonator properties} for an impersonated
value. An @tech{impersonator property} is similar to a @tech{structure
type property}, but it applies to impersonators instead of structure
types and their instances.
@defproc[(impersonator? [v any/c]) boolean?]{
Returns @racket[#t] if @racket[v] is an @tech{impersonator}, @racket[#f] otherwise.
Programs and libraries generally should avoid @racket[impersonator?] and
treat impersonators the same as non-impersonator values. In rare cases,
@racket[impersonator?] may be needed to guard against redirection by an
impersonator of an operation to an arbitrary procedure.}
@defproc[(chaperone? [v any/c]) boolean?]{
Returns @racket[#t] if @racket[v] is a @tech{chaperone}, @racket[#f] otherwise.
Programs and libraries generally should avoid @racket[chaperone?] for
the same reason that they should avoid @racket[impersonator?].}
@defproc[(impersonator-of? [v1 any/c] [v2 any/c]) boolean?]{
Indicates whether @racket[v1] can be considered equivalent modulo
impersonators to @racket[v2].
Any two values that are @racket[eq?] to one another are also @racket[impersonator-of?].
For values that include no impersonators, @racket[v1] and @racket[v2] are
considered impersonators of each other if they are @racket[equal?].
If at least one of @racket[v1] or @racket[v2] is an impersonator:
@itemlist[
@item{If @racket[v1] impersonates @racket[_v1*] then @racket[(impersonator-of? v1 v2)]
is @racket[#t] if and only if @racket[(impersonator-of? _v1* v2)] is @racket[#t].}
@item{If @racket[v2] is a non-interposing impersonator that impersonates @racket[_v2*], i.e.,
all of its interposition procedures are @racket[#f], then @racket[(impersonator-of? v1 v2)]
is @racket[#t] if and only if @racket[(impersonator-of? v1 _v2*)] is @racket[#t].}
@item{When @racket[v2] is an impersonator constructed with at least one non-@racket[#f] interposition procedure,
but @racket[v1] is not an impersonator then @racket[(impersonator-of? v1 v2)] is @racket[#f].}]}
Otherwise, if neither @racket[_v1] or @racket[_v2] is an impersonator, but either
of them contains an impersonator as a subpart (e.g., @racket[_v1] is a list with
an impersonator as one of its elements), then @racket[(impersonator-of? _v1 _v2)]
proceeds by comparing @racket[_v1] and @racket[_v2] recursively (as with
@racket[equal?]), returning true if all subparts are @racket[impersonator-of?].
@examples[
(impersonator-of? (impersonate-procedure add1 (λ (x) x))
add1)
(impersonator-of? (impersonate-procedure add1 (λ (x) x))
sub1)
(impersonator-of? (impersonate-procedure
(impersonate-procedure add1 (λ (x) x)) (λ (x) x))
add1)
(impersonator-of? (impersonate-procedure add1 (λ (x) x))
(impersonate-procedure add1 #f))
(impersonator-of? (impersonate-procedure add1 (λ (x) x))
(impersonate-procedure add1 (λ (x) x)))
(impersonator-of? (list 1 2)
(list 1 2))
(impersonator-of? (list (impersonate-procedure add1 (λ (x) x)) sub1)
(list add1 sub1))
]
@defproc[(chaperone-of? [v1 any/c] [v2 any/c]) boolean?]{
Indicates whether @racket[v1] can be considered equivalent modulo
chaperones to @racket[v2].
For values that include no chaperones, @racket[v1] and @racket[v2] can
be considered chaperones of each other if they are @racket[equal?],
except that mutable vectors, boxes, strings, byte strings, and mutable
structures within @racket[v1] and @racket[v2] must be @racket[eq?].
Otherwise, chaperones within @racket[v2] must be intact within
@racket[v1] analogous to way that @racket[impersonator-of?] requires
that impersonators are preserved, except that @racket[prop:impersonator-of]
has no analog for @racket[chaperone-of?].}
@defproc[(impersonator-ephemeron [v any/c]) ephemeron?]{
Produces an @tech{ephemeron} that can be used to connect the
reachability of @racket[v] (in the sense of garbage collection; see
@secref["gc-model"]) with the reachability of any value for which
@racket[v] is an @tech{impersonator}. That is, the value @racket[v]
will be considered reachable as long as the result ephemeron is
reachable in addition to any value that @racket[v] impersonates
(including itself).}
@defproc[(procedure-impersonator*? [v any/c]) boolean?]{
Returns @racket[#t] for any procedure impersonator that either was produced by
@racket[impersonate-procedure*] or @racket[chaperone-procedure*], or is
an impersonator/chaperone of a value that was created with
@racket[impersonate-procedure*] or @racket[chaperone-procedure*]
(possibly transitively).}
@; ------------------------------------------------------------
@section{Impersonator Constructors}
@defproc[(impersonate-procedure [proc procedure?]
[wrapper-proc (or/c procedure? #f)]
[prop impersonator-property?]
[prop-val any] ... ...)
(and/c procedure? impersonator?)]{
Returns an impersonator procedure that has the same arity, name, and
other attributes as @racket[proc]. When the impersonator procedure is
applied, the arguments are first passed to @racket[wrapper-proc]
(when it is not @racket[#f]), and
then the results from @racket[wrapper-proc] are passed to
@racket[proc]. The @racket[wrapper-proc] can also supply a procedure
that processes the results of @racket[proc].
The arity of @racket[wrapper-proc] must include the arity of
@racket[proc]. The allowed keyword arguments of @racket[wrapper-proc]
must be a superset of the allowed keywords of @racket[proc]. The
required keyword arguments of @racket[wrapper-proc] must be a subset
of the required keywords of @racket[proc].
For applications without keywords, the result of @racket[wrapper-proc]
must be at least the same number of values as supplied to it.
Additional results can be supplied---before the values that correspond
to the supplied values---in the following pattern:
@itemlist[
@item{An optional procedure, @racket[_result-wrapper-proc], which
will be applied to the results of @racket[proc]; followed by}
@item{any number of repetitions of @racket['mark _key _val] (i.e.,
three values), where the call @racket[_proc] is wrapped to
install a @tech{continuation mark} @racket[_key] and @racket[_val].}
]
If @racket[_result-wrapper-proc] is produced, it must be a procedure
that accepts as many results as produced by @racket[proc]; it must
return the same number of results. If @racket[_result-wrapper-proc] is
not supplied, then @racket[proc] is called in @tech{tail position}
with respect to the call to the impersonator.
For applications that include keyword arguments, @racket[wrapper-proc]
must return an additional value before any other values but after
@racket[_result-wrapper-proc] and @racket['mark _key _val]
sequences (if any). The additional value must be a
list of replacements for the keyword arguments that were supplied to the
impersonator (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.
If @racket[wrapper] is @racket[#f], then applying the resulting
impersonator is the same as applying @racket[proc]. If
@racket[wrapper] is @racket[#f] and no @racket[prop] is provided, then
@racket[proc] is returned and is not impersonated.
Pairs of @racket[prop] and @racket[prop-val] (the number of arguments
to @racket[procedure-impersonator] must be even) add impersonator properties
or override impersonator-property values of @racket[proc].
If any @racket[prop] is @racket[impersonator-prop:application-mark] and if the
associated @racket[prop-val] is a pair, then the call to @racket[proc]
is wrapped with @racket[with-continuation-mark] using @racket[(car
prop-val)] as the mark key and @racket[(cdr prop-val)] as the mark
value. In addition, if the immediate
continuation frame of the call to the impersonated procedure
includes a value for @racket[(car prop-val)]---that is, if
@racket[call-with-immediate-continuation-mark] would produce a value
for @racket[(car prop-val)] in the call's continuation---then the value is
also installed as an immediate value for @racket[(car prop-val)] as a
mark during the call to @racket[wrapper-proc] (which allows tail-calls
of impersonators with respect to wrapping impersonators to be detected within
@racket[wrapper-proc]).
@history[#:changed "6.3.0.5" @elem{Added support for @racket['mark
_key _val] results from
@racket[wrapper-proc].}]
@examples[
(define (add15 x) (+ x 15))
(define add15+print
(impersonate-procedure add15
(λ (x)
(printf "called with ~s\n" x)
(values (λ (res)
(printf "returned ~s\n" res)
res)
x))))
(add15 27)
(add15+print 27)
(define-values (imp-prop:p1 imp-prop:p1? imp-prop:p1-get)
(make-impersonator-property 'imp-prop:p1))
(define-values (imp-prop:p2 imp-prop:p2? imp-prop:p2-get)
(make-impersonator-property 'imp-prop:p2))
(define add15.2 (impersonate-procedure add15 #f imp-prop:p1 11))
(add15.2 2)
(imp-prop:p1? add15.2)
(imp-prop:p1-get add15.2)
(imp-prop:p2? add15.2)
(define add15.3 (impersonate-procedure add15.2 #f imp-prop:p2 13))
(add15.3 3)
(imp-prop:p1? add15.3)
(imp-prop:p1-get add15.3)
(imp-prop:p2? add15.3)
(imp-prop:p2-get add15.3)
(define add15.4 (impersonate-procedure add15.3 #f imp-prop:p1 101))
(add15.4 4)
(imp-prop:p1? add15.4)
(imp-prop:p1-get add15.4)
(imp-prop:p2? add15.4)
(imp-prop:p2-get add15.4)]
}
@defproc[(impersonate-procedure* [proc procedure?]
[wrapper-proc (or/c procedure? #f)]
[prop impersonator-property?]
[prop-val any] ... ...)
(and/c procedure? impersonator?)]{
Like @racket[impersonate-procedure], except that @racket[wrapper-proc]
receives an additional argument before all other arguments. The
additional argument is the procedure @racket[_orig-proc] that was
originally applied.
If the result of @racket[impersonate-procedure*] is applied directly,
then @racket[_orig-proc] is that result. If the result is further
impersonated before being applied, however, @racket[_orig-proc] is the
further impersonator.
An @racket[_orig-proc] argument might be useful so that
@racket[wrapper-proc] can extract @tech{impersonator properties}
that are overridden by further impersonators, for example.
@history[#:added "6.1.1.5"]}
@defproc[(impersonate-struct [v any/c]
[struct-type struct-type? _unspecified]
[orig-proc (or/c struct-accessor-procedure?
struct-mutator-procedure?
struct-type-property-accessor-procedure?)]
[redirect-proc (or/c procedure? #f)] ... ...
[prop impersonator-property?]
[prop-val any] ... ...)
any/c]{
Returns an impersonator of @racket[v], which redirects certain
operations on the impersonated value. The @racket[orig-proc]s
indicate the operations to redirect, and the corresponding
@racket[redirect-proc]s supply the redirections. The optional
@racket[struct-type] argument, when provided, acts as a witness for
the representation of @racket[v], which must be an instance of
@racket[struct-type].
The protocol for a @racket[redirect-proc] depends on the corresponding
@racket[orig-proc], where @racket[_self] refers to the value to which
@racket[orig-proc] is originally applied:
@itemlist[
@item{A structure-field accessor: @racket[redirect-proc]
must accept two arguments, @racket[_self] and the value
@racket[_field-v] that @racket[orig-proc] produces for
@racket[v]; it must return a replacement for
@racket[_field-v]. The corresponding field must not be
immutable, and either the field's structure type must be
accessible via the current @tech{inspector} or one of the other
@racket[orig-proc]s must be a structure-field mutator for the
same field.}
@item{A structure-field mutator: @racket[redirect-proc] must accept
two arguments, @racket[_self] and the value @racket[_field-v]
supplied to the mutator; it must return a replacement for
@racket[_field-v] to be propagated to @racket[orig-proc] and
@racket[v].}
@item{A property accessor: @racket[redirect-proc] uses the same
protocol as for a structure-field accessor. The accessor's
property must have been created with @racket['can-impersonate]
as the second argument to @racket[make-struct-type-property].}
]
When a @racket[redirect-proc] is @racket[#f], the corresponding
@racket[orig-proc] is unaffected. Supplying @racket[#f] for a
@racket[redirect-proc] is useful to allow its @racket[orig-proc] to
act as a ``witness'' of @racket[v]'s representation and enable the
addition of @racket[prop]s.
Pairs of @racket[prop] and @racket[prop-val] (the number of arguments
to @racket[impersonate-struct] must be odd) add impersonator properties
or override impersonator-property values of @racket[v].
Each @racket[orig-proc] must indicate a distinct operation. If no
@racket[struct-type] and no @racket[orig-proc]s are supplied, then no @racket[prop]s must be
supplied. If @racket[orig-proc]s are supplied only with @racket[#f]
@racket[redirect-proc]s and no @racket[prop]s are supplied, then
@racket[v] is returned and is not impersonated.
If any @racket[orig-proc] is itself an impersonator, then a use of the
accessor or mutator that @racket[orig-proc] impersonates is redirected
for the resulting impersonated structure to use @racket[orig-proc] on
@racket[v] before @racket[redirect-proc] (in the case of accessor) or
after @racket[redirect-proc] (in the case of a mutator).
@history[#:changed "6.1.1.2" @elem{Changed first argument to an
accessor or mutator
@racket[redirect-proc] from
@racket[v] to @racket[_self].}
#:changed "6.1.1.8" @elem{Added optional @racket[struct-type]
argument.}]}
@defproc[(impersonate-vector [vec (and/c vector? (not/c immutable?))]
[ref-proc (or/c (vector? exact-nonnegative-integer? any/c . -> . any/c) #f)]
[set-proc (or/c (vector? exact-nonnegative-integer? any/c . -> . any/c) #f)]
[prop impersonator-property?]
[prop-val any] ... ...)
(and/c vector? impersonator?)]{
Returns an impersonator of @racket[vec], which redirects the
@racket[vector-ref] and @racket[vector-set!] operations.
The @racket[ref-proc] and @racket[set-proc] arguments must either both be procedures
or both be @racket[#f]. If they are @racket[#f] then @racket[impersonate-vector] does not interpose
on @racket[vec], but still allows attaching impersonator properties.
If @racket[ref-proc] is a procedure it must accept @racket[vec], an index passed to
@racket[vector-ref], and the value that @racket[vector-ref] on
@racket[vec] produces for the given index; it must produce a
replacement for the value, which is the result of @racket[vector-ref]
on the impersonator.
If @racket[set-proc] is a procedure it must accept @racket[vec], an index passed to
@racket[vector-set!], and the value passed to @racket[vector-set!]; it
must produce a replacement for the value, which is used
with @racket[vector-set!] on the original @racket[vec] to install the
value.
Pairs of @racket[prop] and @racket[prop-val] (the number of arguments
to @racket[impersonate-vector] must be odd) add impersonator properties
or override impersonator-property values of @racket[vec].
@history[#:changed "6.9.0.2"]{Added non-interposing vector impersonators.}
}
@defproc[(impersonate-vector* [vec (and/c vector? (not/c immutable?))]
[ref-proc (or/c (vector? vector? exact-nonnegative-integer? any/c . -> . any/c) #f)]
[set-proc (or/c (vector? vector? exact-nonnegative-integer? any/c . -> . any/c) #f)]
[prop impersonator-property?]
[prop-val any] ... ...)
(and/c vector? impersonator?)]{
Like @racket[impersonate-vector], except that @racket[ref-proc] and @racket[set-proc] each receive
an additional vector as argument before other arguments. The additional argument is the original
impersonated vector, access to which triggered interposition in the first place.
The additional vector argument might be useful so that @racket[ref-proc] or @racket[set-proc]
can extract impersonator properties that are overridden by further impersonators, for example.
@history[#:added "6.9.0.2"]
}
@defproc[(impersonate-box [box (and/c box? (not/c immutable?))]
[unbox-proc (box? any/c . -> . any/c)]
[set-proc (box? any/c . -> . any/c)]
[prop impersonator-property?]
[prop-val any] ... ...)
(and/c box? impersonator?)]{
Returns an impersonator of @racket[box], which redirects the
@racket[unbox] and @racket[set-box!] operations.
The @racket[unbox-proc] must accept @racket[box] and the value that
@racket[unbox] produces on @racket[box]; it must produce a replacement
value, which is the result of @racket[unbox] on the impersonator.
The @racket[set-proc] must accept @racket[box] and the value passed to
@racket[set-box!]; it must produce a replacement
value, which is used with @racket[set-box!] on the original
@racket[box] to install the value.
Pairs of @racket[prop] and @racket[prop-val] (the number of arguments
to @racket[impersonate-box] must be odd) add impersonator properties
or override impersonator-property values of @racket[box].}
@defproc[(impersonate-hash [hash (and/c hash? (not/c immutable?))]
[ref-proc (hash? any/c . -> . (values
any/c
(hash? any/c any/c . -> . any/c)))]
[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)]
[clear-proc (or/c #f (hash? . -> . any)) #f]
[equal-key-proc (or/c #f (hash? any/c . -> . any/c)) #f]
[prop impersonator-property?]
[prop-val any] ... ...)
(and/c hash? impersonator?)]{
Returns an impersonator of @racket[hash], which redirects the
@racket[hash-ref], @racket[hash-set!] or @racket[hash-set] (as
applicable), @racket[hash-remove] or @racket[hash-remove!] (as
applicable), @racket[hash-clear] or @racket[hash-clear!] (as
applicable and if @racket[clear-proc] is not @racket[#f]) operations. When
@racket[hash-set], @racket[hash-remove] or @racket[hash-clear] is used on an impersonator of a hash
table, the result is an impersonator with the same redirecting procedures.
In addition, operations like
@racket[hash-iterate-key] or @racket[hash-map], which extract
keys from the table, use @racket[key-proc] to filter keys extracted
from the table. Operations like @racket[hash-iterate-value] or
@racket[hash-values] implicitly use @racket[hash-ref] and
therefore redirect through @racket[ref-proc].
The @racket[ref-proc] must accept @racket[hash] and a key passed
to @racket[hash-ref]. It must return a replacement key
as well as a procedure. The returned procedure is called only if the
returned key is found in @racket[hash] via @racket[hash-ref], in which
case the procedure is called with @racket[hash], the previously
returned key, and the found value. The returned procedure must itself
return a replacement for the found value.
The @racket[set-proc] must accept @racket[hash], a key passed to
@racket[hash-set!] or @racket[hash-set], and the value passed to
@racket[hash-set!] or @racket[hash-set]; it must produce two values: a
replacement for the key and a replacement for the value. The returned
key and value are used with @racket[hash-set!] or @racket[hash-set] on
the original @racket[hash] to install the value.
The @racket[remove-proc] must accept @racket[hash] and a key passed to
@racket[hash-remove!] or @racket[hash-remove]; it must produce the a
replacement for the key, which is used with @racket[hash-remove!] or
@racket[hash-remove] on the original @racket[hash] to remove any
mapping using the (impersonator-replaced) key.
The @racket[key-proc] must accept @racket[hash] and a key that has
been extracted from @racket[hash] (by @racket[hash-iterate-key] or
other operations that use @racket[hash-iterate-key] internally); it
must produce a replacement for the key, which is then reported as a
key extracted from the table.
If @racket[clear-proc] is not @racket[#f], it must accept
@racket[hash] as an argument, and its result is ignored. The fact that
@racket[clear-proc] returns (as opposed to raising an exception or
otherwise escaping) grants the capability to remove all keys from @racket[hash].
If @racket[clear-proc] is @racket[#f], then @racket[hash-clear] or
@racket[hash-clear!] on the impersonator is implemented using
@racket[hash-iterate-key] and @racket[hash-remove] or @racket[hash-remove!].
If @racket[equal-key-proc] is not @racket[#f], it effectively
interposes on calls to @racket[equal?], @racket[equal-hash-code], and
@racket[equal-secondary-hash-code] for the keys of @racket[hash]. The
@racket[equal-key-proc] must accept as its arguments @racket[hash] and
a key that is either mapped by @racket[hash] or passed to
@racket[hash-ref], etc., where the latter has potentially been
adjusted by the corresponding @racket[ref-proc], etc@|.__| The result
is a value that is passed to @racket[equal?],
@racket[equal-hash-code], and @racket[equal-secondary-hash-code] as
needed to hash and compare keys. In the case of @racket[hash-set!] or
@racket[hash-set], the key that is passed to @racket[equal-key-proc]
is the one stored in the hash table for future lookup.
The @racket[hash-iterate-value], @racket[hash-map], or
@racket[hash-for-each] functions use a combination of
@racket[hash-iterate-key] and @racket[hash-ref]. If a key
produced by @racket[key-proc] does not yield a value through
@racket[hash-ref], then the @exnraise[exn:fail:contract].
Pairs of @racket[prop] and @racket[prop-val] (the number of arguments
to @racket[impersonate-hash] must be odd) add impersonator properties
or override impersonator-property values of @racket[hash].
In the case of an immutable hash table, two impersonated hash tables count as
``the same value'' (for purposes of @racket[impersonator-of?]) when their
redirection procedures were originally attached to a hash table by the same
call to @racket[impersonate-hash] or @racket[chaperone-hash] (and potentially
propagated by @racket[hash-set], @racket[hash-remove], or @racket[hash-clear]),
as long as the content of the first hash table is @racket[impersonator-of?] of
the second hash table.
@history[#:changed "6.3.0.11" @elem{Added the @racket[equal-key-proc]
argument.}]}
@defproc[(impersonate-channel [channel channel?]
[get-proc (channel? . -> . (values channel? (any/c . -> . any/c)))]
[put-proc (channel? any/c . -> . any/c)]
[prop impersonator-property?]
[prop-val any] ... ...)
(and/c channel? impersonator?)]{
Returns an impersonator of @racket[channel], which redirects the
@racket[channel-get] and @racket[channel-put] operations.
The @racket[get-proc] generator is called on @racket[channel-get]
or any other operation that fetches results from the channel (such
as a @racket[sync] on the channel). The @racket[get-proc] must return
two values: a @tech{channel} that is an impersonator of @racket[channel], and a
procedure that is used to check the channel's contents.
The @racket[put-proc] must accept @racket[channel] and the value passed to
@racket[channel-put]; it must produce a replacement
value, which is used with @racket[channel-put] on the original
@racket[channel] to send the value over the channel.
Pairs of @racket[prop] and @racket[prop-val] (the number of arguments
to @racket[impersonate-channel] must be odd) add impersonator properties
or override impersonator-property values of @racket[channel].}
@defproc[(impersonate-prompt-tag [prompt-tag continuation-prompt-tag?]
[handle-proc procedure?]
[abort-proc procedure?]
[cc-guard-proc procedure? values]
[callcc-impersonate-proc (procedure? . -> . procedure?) (lambda (p) p)]
[prop impersonator-property?]
[prop-val any] ... ...)
(and/c continuation-prompt-tag? impersonator?)]{
Returns an impersonator of @racket[prompt-tag], which redirects
the @racket[call-with-continuation-prompt] and
@racket[abort-current-continuation] operations.
The @racket[handle-proc] must accept the values that the handler
of a continuation prompt would take and it must produce replacement
values, which will be passed to the handler.
The @racket[abort-proc] must accept the values passed to
@racket[abort-current-continuation]; it must produce replacement
values, which are aborted to the appropriate prompt.
The @racket[cc-guard-proc] must accept the values produced by
@racket[call-with-continuation-prompt] in the case that a
non-composable continuation is applied to replace the continuation
that is delimited by the prompt, but only if
@racket[abort-current-continuation] is not later used to abort the
continuation delimited by the prompt (in which case
@racket[abort-proc] is used).
The @racket[callcc-impersonate-proc] must accept a procedure that
guards the result of a continuation captured by
@racket[call-with-current-continuation] with the impersonated prompt
tag. The @racket[callcc-impersonate-proc] is applied (under a
@tech{continuation barrier}) when the captured continuation is applied
to refine a guard function (initially @racket[values]) that is
specific to the delimiting prompt; this prompt-specific guard is
ultimately composed with any @racket[cc-guard-proc] that is in effect
at the delimiting prompt, and it is not used in the same case that a
@racket[cc-guard-proc] is not used (i.e., when
@racket[abort-current-continuation] is used to abort to the
prompt). In the special case where the delimiting prompt at
application time is a thread's built-in initial prompt,
@racket[callcc-impersonate-proc] is ignored (partly on the grounds
that the initial prompt's result is ignored).
Pairs of @racket[prop] and @racket[prop-val] (the number of arguments
to @racket[impersonate-prompt-tag] must be odd) add impersonator properties
or override impersonator-property values of @racket[prompt-tag].
@examples[
(define tag
(impersonate-prompt-tag
(make-continuation-prompt-tag)
(lambda (n) (* n 2))
(lambda (n) (+ n 1))))
(call-with-continuation-prompt
(lambda ()
(abort-current-continuation tag 5))
tag
(lambda (n) n))
]
}
@defproc[(impersonate-continuation-mark-key
[key continuation-mark-key?]
[get-proc procedure?]
[set-proc procedure?]
[prop impersonator-property?]
[prop-val any] ... ...)
(and/c continuation-mark? impersonator?)]{
Returns an impersonator of @racket[key], which redirects
@racket[with-continuation-mark] and continuation mark accessors such
as @racket[continuation-mark-set->list].
The @racket[get-proc] must accept the value attached to a
continuation mark and it must produce a replacement
value, which will be returned by the continuation mark accessor.
The @racket[set-proc] must accept a value passed to
@racket[with-continuation-mark]; it must produce a replacement
value, which is attached to the continuation frame.
Pairs of @racket[prop] and @racket[prop-val] (the number of arguments
to @racket[impersonate-prompt-tag] must be odd) add impersonator properties
or override impersonator-property values of @racket[key].
@examples[
(define mark-key
(impersonate-continuation-mark-key
(make-continuation-mark-key)
(lambda (l) (map char-upcase l))
(lambda (s) (string->list s))))
(with-continuation-mark mark-key "quiche"
(continuation-mark-set-first
(current-continuation-marks)
mark-key))
]
}
@defthing[prop:impersonator-of struct-type-property?]{
A @tech{structure type property} (see @secref["structprops"]) that
supplies a procedure for extracting an impersonated value from a structure
that represents an impersonator. The property is used for @racket[impersonator-of?]
as well as @racket[equal?].
The property value must be a procedure of one argument, which is a
structure whose structure type has the property. The result can be
@racket[#f] to indicate the structure does not represent an impersonator,
otherwise the result is a value for which the original structure is an
impersonator (so the original structure is an @racket[impersonator-of?] and
@racket[equal?] to the result value). The result value must have the
same @racket[prop:impersonator-of] and @racket[prop:equal+hash] property
values as the original structure, if any, and the property values must be
inherited from the same structure type (which ensures some consistency
between @racket[impersonator-of?] and @racket[equal?]).
@tech{Impersonator property} predicates and accessors applied to a
structure with the @racket[prop:impersonator-of] property first check
for the property on the immediate structure, and if it is not found,
the value produced by the @racket[prop:impersonator-of] procedure is
checked (recursively).
@history[#:changed "6.1.1.8" @elem{Made @tech{impersonator property}
predicates and accessors sensitive
to @racket[prop:impersonator-of].}]}
@defthing[prop:authentic struct-type-property?]{
A @tech{structure type property} that declares a structure type as
@deftech{authentic}. The value associated with the property is ignored;
the presence of the property itself makes the structure type
authentic.
Instances of an @tech{authentic} structure type cannot be impersonated
via @racket[impersonate-struct] or chaperoned via
@racket[chaperone-struct]. As a consequence, an instance of an
@tech{authentic} structure type can be given a contract (see
@racket[struct/c]) only if it is a @tech{flat contract}.
Declaring a structure type as @tech{authentic} can prevent unwanted
structure impersonation, but exposed structure types normally should
support impersonators or chaperones to facilitate contracts. Declaring
a structure type as @tech{authentic} can also slightly improve the
performance of structure predicates, selectors, and mutators, which
can be appropriate for data structures that are private
and frequently used within a library.
@history[#:added "6.9.0.4"]}
@; ------------------------------------------------------------
@section{Chaperone Constructors}
@defproc[(chaperone-procedure [proc procedure?]
[wrapper-proc (or/c procedure? #f)]
[prop impersonator-property?]
[prop-val any] ... ...)
(and/c procedure? chaperone?)]{
Like @racket[impersonate-procedure], but for each value supplied to
@racket[wrapper-proc], the corresponding result must be the same or a
chaperone of (in the sense of @racket[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
@racket[proc]; it must return the same number of results, each of
which is the same or a chaperone of the corresponding original result.
For applications that include keyword arguments, @racket[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
chaperone 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.}
@defproc[(chaperone-procedure* [proc procedure?]
[wrapper-proc (or/c procedure? #f)]
[prop impersonator-property?]
[prop-val any] ... ...)
(and/c procedure? chaperone?)]{
Like @racket[chaperone-procedure], but @racket[wrapper-proc] receives
an extra argument as with @racket[impersonate-procedure*].
@history[#:added "6.1.1.5"]}
@defproc[(chaperone-struct [v any/c]
[struct-type struct-type? _unspecified]
[orig-proc (or/c struct-accessor-procedure?
struct-mutator-procedure?
struct-type-property-accessor-procedure?
(one-of/c struct-info))]
[redirect-proc (or/c procedure? #f)] ... ...
[prop impersonator-property?]
[prop-val any] ... ...)
any/c]{
Like @racket[impersonate-struct], but with the following refinements,
where @racket[_self] refers to the value to which
a @racket[orig-proc] is originally applied:
@itemlist[
@item{With a structure-field accessor as @racket[orig-proc],
@racket[redirect-proc] must accept two arguments, @racket[_self] and
the value @racket[_field-v] that @racket[orig-proc] produces for
@racket[v]; it must return a chaperone of @racket[_field-v]. The
corresponding field may be immutable.}
@item{With structure-field mutator as @racket[orig-proc],
@racket[redirect-proc] must accept two arguments, @racket[_self] and
the value @racket[_field-v] supplied to the mutator; it must
return a chaperone of @racket[_field-v] to be propagated to
@racket[orig-proc] and @racket[v].}
@item{A property accessor can be supplied as @racket[orig-proc], and
the property need not have been created with
@racket['can-impersonate]. The corresponding
@racket[redirect-proc] uses the same protocol as for a
structure-field accessor.}
@item{With @racket[struct-info] as @racket[orig-proc], the
corresponding @racket[redirect-proc] must accept two values,
which are the results of @racket[struct-info] on @racket[v]; it
must return each values or a chaperone of each value. The
@racket[redirect-proc] is not called if @racket[struct-info]
would return @racket[#f] as its first argument. An
@racket[orig-proc] can be @racket[struct-info] only if
@racket[struct-type] or some other @racket[orig-proc] is supplied.}
@item{Any accessor or mutator @racket[orig-proc] that is an
@tech{impersonator} must be specifically a @tech{chaperone}.}
]
Supplying a property accessor for @racket[orig-proc] enables
@racket[prop] arguments, the same as supplying an accessor, mutator,
or structure type.
@history[#:changed "6.1.1.2" @elem{Changed first argument to an
accessor or mutator
@racket[redirect-proc] from
@racket[v] to @racket[_self].}
#:changed "6.1.1.8" @elem{Added optional @racket[struct-type]
argument.}]}
@defproc[(chaperone-vector [vec vector?]
[ref-proc (or/c (vector? exact-nonnegative-integer? any/c . -> . any/c) #f)]
[set-proc (or/c (vector? exact-nonnegative-integer? any/c . -> . any/c) #f)]
[prop impersonator-property?]
[prop-val any] ... ...)
(and/c vector? chaperone?)]{
Like @racket[impersonate-vector], but with support for immutable vectors. The
@racket[ref-proc] procedure must produce the same value or a chaperone
of the original value, and @racket[set-proc] must produce the value
that is given or a chaperone of the value. The @racket[set-proc] will
not be used if @racket[vec] is immutable.}
@defproc[(chaperone-vector* [vec (and/c vector? (not/c immutable?))]
[ref-proc (or/c (vector? vector? exact-nonnegative-integer? any/c . -> . any/c) #f)]
[set-proc (or/c (vector? vector? exact-nonnegative-integer? any/c . -> . any/c) #f)]
[prop impersonator-property?]
[prop-val any] ... ...)
(and/c vector? chaperone?)]{
Like @racket[chaperone-vector], but @racket[ref-proc] and @racket[set-proc] receive an extra argument
as with @racket[impersonate-vector*].
@history[#:added "6.9.0.2"]
}
@defproc[(chaperone-box [box box?]
[unbox-proc (box? any/c . -> . any/c)]
[set-proc (box? any/c . -> . any/c)]
[prop impersonator-property?]
[prop-val any] ... ...)
(and/c box? chaperone?)]{
Like @racket[impersonate-box], but with support for immutable boxes. The
@racket[unbox-proc] procedure must produce the same value or a
chaperone of the original value, and @racket[set-proc] must produce
the same value or a chaperone of the value that it is given. The
@racket[set-proc] will not be used if @racket[box] is immutable.}
@defproc[(chaperone-hash [hash hash?]
[ref-proc (hash? any/c . -> . (values
any/c
(hash? any/c any/c . -> . any/c)))]
[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)]
[clear-proc (or/c #f (hash? . -> . any)) #f]
[equal-key-proc (or/c #f (hash? any/c . -> . any/c)) #f]
[prop impersonator-property?]
[prop-val any] ... ...)
(and/c hash? chaperone?)]{
Like @racket[impersonate-hash], but with constraints on the given functions
and support for immutable hashes. The @racket[ref-proc] procedure must
return a found value or a chaperone of the value. The
@racket[set-proc] procedure must produce two values: the key that it
is given or a chaperone of the key and the value that it is given or a
chaperone of the value. The @racket[remove-proc], @racket[key-proc],
and @racket[equal-key-proc]
procedures must produce the given key or a chaperone of the key.
@history[#:changed "6.3.0.11" @elem{Added the @racket[equal-key-proc]
argument.}]}
@defproc[(chaperone-struct-type [struct-type struct-type?]
[struct-info-proc procedure?]
[make-constructor-proc (procedure? . -> . procedure?)]
[guard-proc procedure?]
[prop impersonator-property?]
[prop-val any] ... ...)
(and/c struct-type? chaperone?)]{
Returns a chaperoned value like @racket[struct-type], but with
@racket[struct-type-info] and @racket[struct-type-make-constructor]
operations on the chaperoned structure type redirected. In addition,
when a new structure type is created as a subtype of the chaperoned
structure type, @racket[guard-proc] is interposed as an extra guard on
creation of instances of the subtype.
The @racket[struct-info-proc] must accept 8 arguments---the result of
@racket[struct-type-info] on @racket[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
@racket[struct-type-info] for the chaperoned structure type.
The @racket[make-constructor-proc] must accept a single procedure
argument, which is a constructor produced by
@racket[struct-type-make-constructor] on @racket[struct-type]. It must
return the same or a chaperone of the procedure, which is used as the
result of @racket[struct-type-make-constructor] on the chaperoned
structure type.
The @racket[guard-proc] must accept as many argument as a constructor
for @racket[struct-type]; it must return the same number of arguments,
each the same or a chaperone of the corresponding argument. The
@racket[guard-proc] is added as a constructor guard when a subtype is
created of the chaperoned structure type.
Pairs of @racket[prop] and @racket[prop-val] (the number of arguments
to @racket[chaperone-struct-type] must be even) add impersonator properties
or override impersonator-property values of @racket[struct-type].}
@defproc[(chaperone-evt [evt evt?]
[proc (evt? . -> . (values evt? (any/c . -> . any/c)))]
[prop impersonator-property?]
[prop-val any] ... ...)
(and/c evt? chaperone?)]{
Returns a chaperoned value like @racket[evt], but with @racket[proc]
as an event generator when the result is synchronized with functions
like @racket[sync].
The @racket[proc] generator is called on synchronization, much like
the procedure passed to @racket[guard-evt], except that @racket[proc]
is given @racket[evt]. The @racket[proc] must return two values: a
@tech{synchronizable event} that is a chaperone of @racket[evt], and a
procedure that is used to check the event's result if it is chosen in
a selection. The latter procedure accepts the result of @racket[evt],
and it must return a chaperone of that value.
Pairs of @racket[prop] and @racket[prop-val] (the number of arguments
to @racket[chaperone-evt] must be even) add impersonator properties
or override impersonator-property values of @racket[evt].}
@defproc[(chaperone-channel [channel channel?]
[get-proc (channel? . -> . (values channel? (any/c . -> . any/c)))]
[put-proc (channel? any/c . -> . any/c)]
[prop impersonator-property?]
[prop-val any] ... ...)
(and/c channel? chaperone?)]{
Like @racket[impersonate-channel], but with restrictions on the
@racket[get-proc] and @racket[put-proc] procedures.
The @racket[get-proc] must return two values: a @tech{channel}
that is a chaperone of @racket[channel], and a procedure that
is used to check the channel's contents. The latter procedure
must return the original value or a chaperone of that value.
The @racket[put-proc] must produce a replacement value that is
either the original value communicated on the channel or a
chaperone of that value.
Pairs of @racket[prop] and @racket[prop-val] (the number of arguments
to @racket[chaperone-channel] must be odd) add impersonator properties
or override impersonator-property values of @racket[channel].}
@defproc[(chaperone-prompt-tag [prompt-tag continuation-prompt-tag?]
[handle-proc procedure?]
[abort-proc procedure?]
[cc-guard-proc procedure? values]
[callcc-chaperone-proc (procedure? . -> . procedure?) (lambda (p) p)]
[prop impersonator-property?]
[prop-val any] ... ...)
(and/c continuation-prompt-tag? chaperone?)]{
Like @racket[impersonate-prompt-tag], but produces a chaperoned value.
The @racket[handle-proc] procedure must produce the same values or
chaperones of the original values, @racket[abort-proc] must produce
the same values or chaperones of the values that it is given, and
@racket[cc-guard-proc] must produce the same values or chaperones of
the original result values, and @racket[callcc-chaperone-proc] must
produce a procedure that is a chaperone or the same as the given
procedure.
@examples[
(define bad-chaperone
(chaperone-prompt-tag
(make-continuation-prompt-tag)
(lambda (n) (* n 2))
(lambda (n) (+ n 1))))
(eval:error
(call-with-continuation-prompt
(lambda ()
(abort-current-continuation bad-chaperone 5))
bad-chaperone
(lambda (n) n)))
(define good-chaperone
(chaperone-prompt-tag
(make-continuation-prompt-tag)
(lambda (n) (if (even? n) n (error "not even")))
(lambda (n) (if (even? n) n (error "not even")))))
(call-with-continuation-prompt
(lambda ()
(abort-current-continuation good-chaperone 2))
good-chaperone
(lambda (n) n))
]
}
@defproc[(chaperone-continuation-mark-key
[key continuation-mark-key?]
[get-proc procedure?]
[set-proc procedure?]
[prop impersonator-property?]
[prop-val any] ... ...)
(and/c continuation-mark-key? chaperone?)]{
Like @racket[impersonate-continuation-mark-key], but produces a
chaperoned value. The @racket[get-proc] procedure must produce the
same value or a chaperone of the original value, and @racket[set-proc]
must produce the same value or a chaperone of the value that it is
given.
@examples[
(define bad-chaperone
(chaperone-continuation-mark-key
(make-continuation-mark-key)
(lambda (l) (map char-upcase l))
string->list))
(eval:error
(with-continuation-mark bad-chaperone "timballo"
(continuation-mark-set-first
(current-continuation-marks)
bad-chaperone)))
(define (checker s)
(if (> (string-length s) 5)
s
(error "expected string of length at least 5")))
(define good-chaperone
(chaperone-continuation-mark-key
(make-continuation-mark-key)
checker
checker))
(with-continuation-mark good-chaperone "zabaione"
(continuation-mark-set-first
(current-continuation-marks)
good-chaperone))
]
}
@; ------------------------------------------------------------
@section{Impersonator Properties}
@defproc[(make-impersonator-property [name symbol?])
(values impersonator-property?
(-> any/c boolean?)
(->* (impersonator?) (any/c) any))]{
Creates a new @tech{impersonator property} and returns three values:
@itemize[
@item{an @deftech{impersonator property descriptor}, for use with
@racket[impersonate-procedure], @racket[chaperone-procedure],
and other impersonator constructors;}
@item{an @deftech{impersonator property predicate} procedure, which takes
an arbitrary value and returns @racket[#t] if the value is an
impersonator with a value for the property, @racket[#f]
otherwise;}
@item{an @deftech{impersonator property accessor} procedure, which
returns the value associated with an impersonator for the property;
if a value given to the accessor is not an impersonator or does not
have a value for the property (i.e. if the corresponding impersonator
property predicate returns @racket[#f]), then a second optional argument
to the selector determines its response: the @exnraise[exn:fail:contract]
is if a second argument is not provided, the second argument is tail-called
with zero arguments if it is a procedure, and the second argument is returned
otherwise.}
]}
@defproc[(impersonator-property? [v any/c]) boolean?]{
Returns @racket[#t] if @racket[v] is a @tech{impersonator property
descriptor} value, @racket[#f] otherwise.}
@defproc[(impersonator-property-accessor-procedure? [v any/c]) boolean?]{
Returns @racket[#t] if @racket[v] is an accessor procedure produced
by @racket[make-impersonator-property], @racket[#f] otherwise.}
@defthing[impersonator-prop:application-mark impersonator-property?]{
An @tech{impersonator property} that is recognized by @racket[impersonate-procedure]
and @racket[chaperone-procedure].}