Wrote documentation for new contract interface. Still needs proofreading, etc.
svn: r17961
This commit is contained in:
parent
2e64069d14
commit
a4a25ba1e9
|
@ -889,34 +889,30 @@ Although these projections have the right error behavior,
|
||||||
they are not quite ready for use as contracts, because they
|
they are not quite ready for use as contracts, because they
|
||||||
do not accomodate blame, and do not provide good error
|
do not accomodate blame, and do not provide good error
|
||||||
messages. In order to accomodate these, contracts do not
|
messages. In order to accomodate these, contracts do not
|
||||||
just use simple projections, but use functions that accept
|
just use simple projections, but use functions that accept a
|
||||||
|
@deftech{blame object} encapsulating
|
||||||
the names of two parties that are the candidates for blame,
|
the names of two parties that are the candidates for blame,
|
||||||
as well as a record of the source location where the
|
as well as a record of the source location where the
|
||||||
contract was established and the name of the contract. They
|
contract was established and the name of the contract. They
|
||||||
can then, in turn, pass that information
|
can then, in turn, pass that information
|
||||||
to @scheme[raise-contract-error] to signal a good error
|
to @scheme[raise-blame-error] to signal a good error
|
||||||
message.
|
message.
|
||||||
|
|
||||||
Here is the first of those two projections, rewritten for
|
Here is the first of those two projections, rewritten for
|
||||||
use in the contract system:
|
use in the contract system:
|
||||||
|
|
||||||
@schemeblock[
|
@schemeblock[
|
||||||
(define (int-proj pos neg src-info name positive-position?)
|
(define (int-proj blame)
|
||||||
(lambda (x)
|
(lambda (x)
|
||||||
(if (integer? x)
|
(if (integer? x)
|
||||||
x
|
x
|
||||||
(raise-contract-error
|
(raise-blame-error
|
||||||
|
blame
|
||||||
val
|
val
|
||||||
src-info
|
|
||||||
pos
|
|
||||||
name
|
|
||||||
"expected <integer>, given: ~e"
|
"expected <integer>, given: ~e"
|
||||||
val))))
|
val))))
|
||||||
]
|
]
|
||||||
|
The new argument specifies who is to be blamed for
|
||||||
The first two new arguments specify who is to be blamed for
|
positive and negative contract violations.
|
||||||
positive and negative contract violations,
|
|
||||||
respectively.
|
|
||||||
|
|
||||||
Contracts, in this system, are always
|
Contracts, in this system, are always
|
||||||
established between two parties. One party provides some
|
established between two parties. One party provides some
|
||||||
|
@ -925,28 +921,24 @@ value, also according to the contract. The first is called
|
||||||
the ``positive'' person and the second the ``negative''. So,
|
the ``positive'' person and the second the ``negative''. So,
|
||||||
in the case of just the integer contract, the only thing
|
in the case of just the integer contract, the only thing
|
||||||
that can go wrong is that the value provided is not an
|
that can go wrong is that the value provided is not an
|
||||||
integer. Thus, only the positive argument can ever accrue
|
integer. Thus, only the positive party can ever accrue
|
||||||
blame (and thus only @scheme[pos] is passed
|
blame. The @scheme[raise-blame-error] function always blames
|
||||||
to @scheme[raise-contract-error]).
|
the positive party.
|
||||||
|
|
||||||
Compare that to the projection for our function contract:
|
Compare that to the projection for our function contract:
|
||||||
|
|
||||||
@schemeblock[
|
@schemeblock[
|
||||||
(define (int->int-proj pos neg src-info name positive-position?)
|
(define (int->int-proj blame)
|
||||||
(let ([dom (int-proj neg pos src-info
|
(let ([dom (int-proj (blame-swap blame))]
|
||||||
name (not positive-position?))]
|
[rng (int-proj blame)])
|
||||||
[rng (int-proj pos neg src-info
|
|
||||||
name positive-position?)])
|
|
||||||
(lambda (f)
|
(lambda (f)
|
||||||
(if (and (procedure? f)
|
(if (and (procedure? f)
|
||||||
(procedure-arity-includes? f 1))
|
(procedure-arity-includes? f 1))
|
||||||
(lambda (x)
|
(lambda (x)
|
||||||
(rng (f (dom x))))
|
(rng (f (dom x))))
|
||||||
(raise-contract-error
|
(raise-blame-error
|
||||||
|
blame
|
||||||
val
|
val
|
||||||
src-info
|
|
||||||
pos
|
|
||||||
name
|
|
||||||
"expected a procedure of one argument, given: ~e"
|
"expected a procedure of one argument, given: ~e"
|
||||||
val)))))
|
val)))))
|
||||||
]
|
]
|
||||||
|
@ -956,17 +948,16 @@ where either a non-procedure is supplied to the contract, or
|
||||||
where the procedure does not accept one argument. As with
|
where the procedure does not accept one argument. As with
|
||||||
the integer projection, the blame here also lies with the
|
the integer projection, the blame here also lies with the
|
||||||
producer of the value, which is
|
producer of the value, which is
|
||||||
why @scheme[raise-contract-error] gets @scheme[pos] and
|
why @scheme[raise-blame-error] is passed @scheme[blame] unchanged.
|
||||||
not @scheme[neg] as its argument.
|
|
||||||
|
|
||||||
The checking for the domain and range are delegated to
|
The checking for the domain and range are delegated to
|
||||||
the @scheme[int-proj] function, which is supplied its
|
the @scheme[int-proj] function, which is supplied its
|
||||||
arguments in the first two line of
|
arguments in the first two line of
|
||||||
the @scheme[int->int-proj] function. The trick here is that,
|
the @scheme[int->int-proj] function. The trick here is that,
|
||||||
even though the @scheme[int->int-proj] function always
|
even though the @scheme[int->int-proj] function always
|
||||||
blames what it sees as positive we can reverse the order of
|
blames what it sees as positive we can swap the blame parties by
|
||||||
the @scheme[pos] and @scheme[neg] arguments so that the
|
calling @scheme[blame-swap] on the given @tech{blame object}, replacing
|
||||||
positive becomes the negative.
|
the positive party with the negative party and vice versa.
|
||||||
|
|
||||||
This is not just a cheap trick to get this example to work,
|
This is not just a cheap trick to get this example to work,
|
||||||
however. The reversal of the positive and the negative is a
|
however. The reversal of the positive and the negative is a
|
||||||
|
@ -982,8 +973,8 @@ travelling back from the requiring module to the providing
|
||||||
module! And finally, when the function produces a result,
|
module! And finally, when the function produces a result,
|
||||||
that result flows back in the original
|
that result flows back in the original
|
||||||
direction. Accordingly, the contract on the domain reverses
|
direction. Accordingly, the contract on the domain reverses
|
||||||
the positive and the negative, just like the flow of values
|
the positive and the negative blame parties, just like the flow
|
||||||
reverses.
|
of values reverses.
|
||||||
|
|
||||||
We can use this insight to generalize the function contracts
|
We can use this insight to generalize the function contracts
|
||||||
and build a function that accepts any two contracts and
|
and build a function that accepts any two contracts and
|
||||||
|
@ -991,21 +982,17 @@ returns a contract for functions between them.
|
||||||
|
|
||||||
@schemeblock[
|
@schemeblock[
|
||||||
(define (make-simple-function-contract dom-proj range-proj)
|
(define (make-simple-function-contract dom-proj range-proj)
|
||||||
(lambda (pos neg src-info name positive-position?)
|
(lambda (blame)
|
||||||
(let ([dom (dom-proj neg pos src-info
|
(let ([dom (dom-proj (blame-swap blame))]
|
||||||
name (not positive-position?))]
|
[rng (range-proj blame)])
|
||||||
[rng (range-proj pos neg src-info
|
|
||||||
name positive-position?)])
|
|
||||||
(lambda (f)
|
(lambda (f)
|
||||||
(if (and (procedure? f)
|
(if (and (procedure? f)
|
||||||
(procedure-arity-includes? f 1))
|
(procedure-arity-includes? f 1))
|
||||||
(lambda (x)
|
(lambda (x)
|
||||||
(rng (f (dom x))))
|
(rng (f (dom x))))
|
||||||
(raise-contract-error
|
(raise-blame-error
|
||||||
|
blame
|
||||||
val
|
val
|
||||||
src-info
|
|
||||||
pos
|
|
||||||
name
|
|
||||||
"expected a procedure of one argument, given: ~e"
|
"expected a procedure of one argument, given: ~e"
|
||||||
val))))))
|
val))))))
|
||||||
]
|
]
|
||||||
|
@ -1014,37 +1001,90 @@ Projections like the ones described above, but suited to
|
||||||
other, new kinds of value you might make, can be used with
|
other, new kinds of value you might make, can be used with
|
||||||
the contract library primitives below.
|
the contract library primitives below.
|
||||||
|
|
||||||
@defproc[(make-proj-contract [name any/c]
|
@deftogether[(
|
||||||
[proj (or/c (-> symbol? symbol? any/c any/c any/c)
|
@defproc[(simple-contract
|
||||||
(-> symbol? symbol? any/c any/c boolean? any/c))]
|
[#:name name any/c 'simple-contract]
|
||||||
[first-order-test (-> any/c any/c)])
|
[#:first-order test (-> any/c any/c) (λ (x) #t)]
|
||||||
contract?]{
|
[#:projection proj (-> blame? (-> any/c any/c))
|
||||||
|
(λ (b)
|
||||||
|
(λ (x)
|
||||||
|
(if (test x)
|
||||||
|
x
|
||||||
|
(raise-blame-error
|
||||||
|
b x "expected <~a>, given: ~e" name x))))])
|
||||||
|
contract?]
|
||||||
|
@defproc[(simple-flat-contract
|
||||||
|
[#:name name any/c 'simple-flat-contract]
|
||||||
|
[#:first-order test (-> any/c any/c) (λ (x) #t)]
|
||||||
|
[#:projection proj (-> blame? (-> any/c any/c))
|
||||||
|
(λ (b)
|
||||||
|
(λ (x)
|
||||||
|
(if (test x)
|
||||||
|
x
|
||||||
|
(raise-blame-error
|
||||||
|
b x "expected <~a>, given: ~e" name x))))])
|
||||||
|
flat-contract?]
|
||||||
|
)]{
|
||||||
|
|
||||||
Builds a new contract.
|
These functions build simple procedure-based contracts and flat contracts,
|
||||||
|
respectively. They both take the same set of three optional arguments: a name,
|
||||||
The first argument is the name of the contract. It can be an
|
a first order predicate, and a blame-tracking projection.
|
||||||
arbitrary S-expression. The second is a projection (see
|
|
||||||
above).
|
|
||||||
|
|
||||||
If the projection only takes four arguments, then the
|
The @scheme[name] argument is any value to be rendered using @scheme[display] to
|
||||||
positive position boolean is not passed to it (this is
|
describe the contract when a violation occurs. The default name for simple
|
||||||
for backwards compatibility).
|
higher order contracts is @schemeresult[simple-contract], and for flat contracts
|
||||||
|
is @schemeresult[simple-flat-contract].
|
||||||
|
|
||||||
The final argument is a predicate that is a
|
The first order predicate @scheme[test] can be used to determine which values
|
||||||
conservative, first-order test of a value. It should be a
|
the contract applies to; usually this is the set of values for which the
|
||||||
function that accepts one argument and returns a boolean. If
|
contract fails immediately without any higher-order wrapping. This test is used
|
||||||
it returns @scheme[#f], its argument must be guaranteed to
|
by @scheme[contract-first-order-passes?], and indirectly by @scheme[or/c] to
|
||||||
fail the contract, and the contract should detect this right
|
determine which of multiple higher order contracts to wrap a value with. The
|
||||||
when the projection is invoked. If it returns true,
|
default test accepts any value.
|
||||||
the value may or may not violate the contract, but any
|
|
||||||
violations must not be signaled immediately.
|
The projection @scheme[proj] defines the behavior of applying the contract. It
|
||||||
|
is a curried function of two arguments: the first application accepts a blame
|
||||||
|
object, and the second accepts a value to protect with the contract. The
|
||||||
|
projection must either produce the value, suitably wrapped to enforce any
|
||||||
|
higher-order aspects of the contract, or signal a contract violation using
|
||||||
|
@scheme[raise-blame-error]. The default projection produces an error when the
|
||||||
|
first order test fails, and produces the value unchanged otherwise.
|
||||||
|
|
||||||
|
Projections for flat contracts must fail precisely when the first order test
|
||||||
|
does, and must produce the input value unchanged otherwise. Applying a flat
|
||||||
|
contract may result in either an application of the predicate, or the
|
||||||
|
projection, or both; therefore, the two must be consistent. The existence of a
|
||||||
|
separate projection only serves to provide more specific error messages. Most
|
||||||
|
flat contracts do not need to supply an explicit projection.
|
||||||
|
|
||||||
|
@defexamples[#:eval (contract-eval)
|
||||||
|
(define int/c
|
||||||
|
(simple-flat-contract #:name 'int/c #:first-order integer?))
|
||||||
|
(contract 1 int/c 'positive 'negative)
|
||||||
|
(contract "not one" int/c 'positive 'negative)
|
||||||
|
(int/c 1)
|
||||||
|
(int/c "not one")
|
||||||
|
(define int->int/c
|
||||||
|
(simple-contract
|
||||||
|
#:name 'int->int/c
|
||||||
|
#:first-order
|
||||||
|
(λ (x) (and (procedure? x) (procedure-arity-includes? x 1)))
|
||||||
|
#:projection
|
||||||
|
(λ (b)
|
||||||
|
(let ([domain ((contract-projection int/c) (blame-swap b))]
|
||||||
|
[range ((contract-projection int/c) blame)])
|
||||||
|
(λ (f)
|
||||||
|
(if (and (procedure? f) (procedure-arity-includes? f 1))
|
||||||
|
(λ (x) (range (f (domain x))))
|
||||||
|
(raise-blame-error
|
||||||
|
b f "expected a function of one argument, got: ~e" f)))))))
|
||||||
|
(contract "not fun" int->int/c 'positive 'negative)
|
||||||
|
(define halve (contract (λ (x) (/ x 2)) int->int/c 'positive 'negative))
|
||||||
|
(halve 2)
|
||||||
|
(halve 1)
|
||||||
|
(halve 1/2)
|
||||||
|
]
|
||||||
|
|
||||||
This function is a convenience function, implemented
|
|
||||||
using @scheme[proj-prop], @scheme[name-prop],
|
|
||||||
@scheme[first-order-prop], and @scheme[stronger-prop].
|
|
||||||
Consider using those directly (as well as @scheme[flat-prop] as necessary),
|
|
||||||
as they allow more flexibility
|
|
||||||
and generally produce more efficient contracts.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@defproc[(build-compound-type-name [c/s any/c] ...) any]{
|
@defproc[(build-compound-type-name [c/s any/c] ...) any]{
|
||||||
|
@ -1086,31 +1126,71 @@ contracts. The error messages assume that the function named by
|
||||||
the value cannot be coerced to a contract.
|
the value cannot be coerced to a contract.
|
||||||
}
|
}
|
||||||
|
|
||||||
@defproc[(raise-contract-error [val any/c]
|
@subsection{Blame Objects}
|
||||||
[src-info any/c]
|
|
||||||
[to-blame symbol?]
|
|
||||||
[contract-name any/c]
|
|
||||||
[fmt string?]
|
|
||||||
[arg any/c] ...)
|
|
||||||
any]{
|
|
||||||
|
|
||||||
Signals a contract violation. The first argument is the value that
|
@defproc[(blame? [x any/c]) boolean?]{
|
||||||
failed to satisfy the contract. The second argument is is the
|
This predicate recognizes @tech{blame objects}.
|
||||||
@scheme[src-info] passed to the projection and the third should be
|
}
|
||||||
either @scheme[pos] or @scheme[neg] (typically @scheme[pos], see the
|
|
||||||
beginning of this section) that was passed to the projection. The
|
|
||||||
fourth argument is the @scheme[contract-name] that was passed to the
|
|
||||||
projection and the remaining arguments are used with @scheme[format]
|
|
||||||
to build an actual error message.}
|
|
||||||
|
|
||||||
@;{
|
@deftogether[(
|
||||||
% to document:
|
@defproc[(blame-positive [b blame?]) any/c]
|
||||||
% proj-prop proj-pred? proj-get
|
@defproc[(blame-negative [b blame?]) any/c]
|
||||||
% name-prop name-pred? name-get
|
)]{
|
||||||
% stronger-prop stronger-pred? stronger-get
|
These functions produce printable descriptions of the current positive and
|
||||||
% flat-prop flat-pred? flat-get
|
negative parties of a blame object.
|
||||||
% first-order-prop first-order-get
|
}
|
||||||
% contract-stronger?
|
|
||||||
|
@defproc[(blame-contract [b blame?]) any/c]{
|
||||||
|
This function produces a description of the contract associated with a blame
|
||||||
|
object (the result of @scheme[contract-name]).
|
||||||
|
}
|
||||||
|
|
||||||
|
@defproc[(blame-value [b blame?]) any/c]{
|
||||||
|
This function produces the name of the value to which the contract was applied,
|
||||||
|
or @scheme[#f] if no name was provided.
|
||||||
|
}
|
||||||
|
|
||||||
|
@defproc[(blame-source [b blame?]) srcloc?]{
|
||||||
|
This function produces the source location associated with a contract. If no
|
||||||
|
source location was provided, all fields of the structure will contain
|
||||||
|
@scheme[#f].
|
||||||
|
}
|
||||||
|
|
||||||
|
@defproc[(blame-swap [b blame?]) blame?]{
|
||||||
|
This function swaps the positive and negative parties of a @tech{blame object}.
|
||||||
|
}
|
||||||
|
|
||||||
|
@deftogether[(
|
||||||
|
@defproc[(blame-original? [b blame?]) boolean?]
|
||||||
|
@defproc[(blame-swapped? [b blame?]) boolean?]
|
||||||
|
)]{
|
||||||
|
|
||||||
|
These functions report whether the current blame of a given blame object is the
|
||||||
|
same as in the original contract invocation (possibly of a compound contract
|
||||||
|
containing the current one), or swapped, respectively. Each is the negation of
|
||||||
|
the other; both are provided for convenience and clarity.
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@defproc[(raise-blame-error [b blame?] [x any/c] [fmt string?] [v any/c] ...)
|
||||||
|
none/c]{
|
||||||
|
|
||||||
|
Signals a contract violation. The first argument, @scheme[b], records the
|
||||||
|
current blame information, including positive and negative parties, the name of
|
||||||
|
the contract, the name of the value, and the source location of the contract
|
||||||
|
application. The second argument, @scheme[x], is the value that failed to
|
||||||
|
satisfy the contract. The remaining arguments are a format string,
|
||||||
|
@scheme[fmt], and its arguments, @scheme[v ...], specifying an error message
|
||||||
|
specific to the precise violation.
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@defproc[(exn:fail:contract:blame? [x any/c]) boolean?]{
|
||||||
|
This predicate recognizes exceptions raised by @scheme[raise-blame-error].
|
||||||
|
}
|
||||||
|
|
||||||
|
@defproc[(exn:fail:contract:blame-object [e exn:fail:contract:blame?]) blame?]{
|
||||||
|
This accessor extracts the blame object associated with a contract violation.
|
||||||
}
|
}
|
||||||
|
|
||||||
@subsection{Contracts as structs}
|
@subsection{Contracts as structs}
|
||||||
|
@ -1118,98 +1198,104 @@ to build an actual error message.}
|
||||||
@emph{@bold{Note:}
|
@emph{@bold{Note:}
|
||||||
The interface in this section is unstable and subject to change.}
|
The interface in this section is unstable and subject to change.}
|
||||||
|
|
||||||
A contract is an arbitrary struct that has all of the
|
@para{
|
||||||
struct properties
|
The property @scheme[prop:contract] allows arbitrary structures to act as
|
||||||
(see @secref["structprops"] in the reference manual)
|
contracts. The property @scheme[prop:flat-contract] allows arbitrary structures
|
||||||
in this section
|
to act as flat contracts; @scheme[prop:flat-contract] inherits both
|
||||||
(except that @scheme[flat-prop] is optional).
|
@scheme[prop:contract] and @scheme[prop:procedure], so flat contract structures
|
||||||
|
may also act as general contracts and as predicate procedures.
|
||||||
Generally speaking, the contract should be a struct with
|
|
||||||
fields that specialize the contract in some way and then
|
|
||||||
properties that implement all of the details of checking
|
|
||||||
the contract and reporting errors, etc.
|
|
||||||
|
|
||||||
For example, an @scheme[between/c] contract is a struct that
|
|
||||||
holds the bounds on the number and then has the properties below
|
|
||||||
that inspect the bounds and take the corresponding action
|
|
||||||
(the @scheme[proj-prop] checks the numbers, the @scheme[name-prop]
|
|
||||||
constructs a name to print out for the contract, etc.).
|
|
||||||
|
|
||||||
@deftogether[(@defthing[proj-prop struct-type-property?]
|
|
||||||
@defproc[(proj-pred? [v any/c]) boolean?]{}
|
|
||||||
@defproc[(proj-get [v proj-pred?])
|
|
||||||
(-> proj-prop?
|
|
||||||
(-> symbol? symbol? (or/c #f syntax?) string? boolean?
|
|
||||||
(-> any/c any/c)))]{})]{
|
|
||||||
|
|
||||||
This is the workhorse property that implements the contract.
|
|
||||||
The property should be bound to a function that accepts
|
|
||||||
the struct and then returns a projection, as described
|
|
||||||
in the docs for @scheme[make-proj-contract] above.
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@deftogether[(@defthing[name-prop struct-type-property?]{}
|
|
||||||
@defproc[(name-pred? [v any/c]) boolean?]{}
|
|
||||||
@defproc[(name-get [v name-pred?]) (-> name-pred? printable/c)]{})]{
|
|
||||||
|
|
||||||
This property should be a function that accepts the struct and returns
|
|
||||||
an s-expression representing the name of the property.
|
|
||||||
|
|
||||||
@mz-examples[#:eval (contract-eval)
|
|
||||||
(write (between/c 1 10))
|
|
||||||
(let ([c (between/c 1 10)])
|
|
||||||
((name-get c) c))]
|
|
||||||
|
|
||||||
}
|
|
||||||
@deftogether[(@defthing[stronger-prop struct-type-property?]{}
|
|
||||||
@defproc[(stronger-pred? [v any/c]) boolean?]{}
|
|
||||||
@defproc[(stronger-get [v stronger-pred?]) (-> stronger-pred? stronger-pred? boolean?)]{})]{
|
|
||||||
|
|
||||||
This property is used when optimizing contracts, in order to tell if some contract is stronger than another one.
|
|
||||||
In some situations, if a contract that is already in place is stronger than one about to be put in place,
|
|
||||||
then the new one is ignored.
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@deftogether[(@defthing[flat-prop struct-type-property?]{}
|
@deftogether[(
|
||||||
@defproc[(flat-pred? [v any/c]) boolean?]{}
|
@defthing[prop:contract struct-type-property?]
|
||||||
@defproc[(flat-get [v flat-pred?]) (-> flat-pred? (-> any/c boolean?))]{})]{
|
@defthing[prop:flat-contract struct-type-property?]
|
||||||
|
)]{
|
||||||
This property should only be present if the contract is a flat contract. In the case that it is
|
These properties declare structures to be contracts or flat contracts,
|
||||||
a flat contract, the value of the property should be a predicate that determines if the
|
respectively. The value for @scheme[prop:contract] must be a @tech{contract
|
||||||
contract holds.
|
property} constructed by @scheme[build-contract-property]; likewise, the value
|
||||||
|
for @scheme[prop:flat-contract] must be a @tech{flat contract property}
|
||||||
@mz-examples[#:eval (contract-eval)
|
constructed by @scheme[build-flat-contract-property].
|
||||||
(flat-pred? (-> integer? integer?))
|
|
||||||
(let* ([c (between/c 1 10)]
|
|
||||||
[pred ((flat-get c) c)])
|
|
||||||
(list (pred 9)
|
|
||||||
(pred 11)))]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@deftogether[(@defthing[first-order-prop struct-type-property?]{}
|
@deftogether[(
|
||||||
@defproc[(first-order-pred? [v any/c]) boolean?]{}
|
@defproc[(build-flat-contract-property
|
||||||
@defproc[(first-order-get [v proj-pred?]) (-> first-order-pred? (-> any/c boolean?))]{})]{
|
[#:name
|
||||||
|
get-name
|
||||||
|
(-> contract? any/c)
|
||||||
|
(λ (c) 'anonymous-flat-contract)]
|
||||||
|
[#:first-order
|
||||||
|
get-first-order
|
||||||
|
(-> contract? (-> any/c boolean?))
|
||||||
|
(λ (c) (λ (x) #t))]
|
||||||
|
[#:projection
|
||||||
|
get-projection
|
||||||
|
(-> contract? (-> blame? (-> any/c any/c)))
|
||||||
|
(λ (c)
|
||||||
|
(λ (b)
|
||||||
|
(λ (x)
|
||||||
|
(if ((get-first-order c) x)
|
||||||
|
x
|
||||||
|
(raise-blame-error
|
||||||
|
b x "expected <~a>, given: ~e" (get-name c) x)))))])
|
||||||
|
flat-contract-property?]
|
||||||
|
@defproc[(build-contract-property
|
||||||
|
[#:name
|
||||||
|
get-name
|
||||||
|
(-> contract? any/c)
|
||||||
|
(λ (c) 'anonymous-contract)]
|
||||||
|
[#:first-order
|
||||||
|
get-first-order
|
||||||
|
(-> contract? (-> any/c boolean?))
|
||||||
|
(λ (c) (λ (x) #t))]
|
||||||
|
[#:projection
|
||||||
|
get-projection
|
||||||
|
(-> contract? (-> blame? (-> any/c any/c)))
|
||||||
|
(λ (c)
|
||||||
|
(λ (b)
|
||||||
|
(λ (x)
|
||||||
|
(if ((get-first-order c) x)
|
||||||
|
x
|
||||||
|
(raise-blame-error
|
||||||
|
b x "expected <~a>, given: ~e" (get-name c) x)))))])
|
||||||
|
contract-property?]
|
||||||
|
)]{
|
||||||
|
|
||||||
This property is used with @scheme[or/c] to determine which branch of the
|
These functions build the arguments for @scheme[prop:contract] and
|
||||||
@scheme[or/c] applies. These don't have to be precise (i.e., returning @scheme[#f] is always safe),
|
@scheme[prop:flat-contract], respectively.
|
||||||
but the more often a contract can honestly return @scheme[#t], the more often
|
|
||||||
it will work with @scheme[or/c].
|
|
||||||
|
|
||||||
For example, function contracts typically check arity in their @scheme[first-order-prop]s.
|
|
||||||
|
|
||||||
|
A @deftech{contract property} specifies the behavior of a structure when used as
|
||||||
|
a contract. It is specified in terms of three accessors: @scheme[get-name],
|
||||||
|
which produces a description to @scheme[display] during a contract violation;
|
||||||
|
@scheme[get-first-order], which produces a first order predicate to be used by
|
||||||
|
@scheme[contract-first-order-passes?]; and @scheme[get-projection], which
|
||||||
|
produces a blame-tracking projection defining the behavior of the contract.
|
||||||
|
These accessors are passed as (optional) keyword arguments to
|
||||||
|
@scheme[build-contract-property], and are applied to instances of the
|
||||||
|
appropriate structure type by the contract system. Their results are used
|
||||||
|
analogously to the arguments of @scheme[simple-contract].
|
||||||
|
|
||||||
|
A @deftech{flat contract property} specifies the behavior of a structure when
|
||||||
|
used as a flat contract. It is specified using
|
||||||
|
@scheme[build-flat-contract-property], and accepts exactly the same set of
|
||||||
|
arguments as @scheme[build-contract-property]. The only difference is that the
|
||||||
|
projection accessor is expected not to wrap its argument in a higher order
|
||||||
|
fashion, analogous to the constraint on projections in
|
||||||
|
@scheme[simple-flat-contract].
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@deftogether[(
|
||||||
|
@defproc[(contract-property? [x any/c]) boolean?]
|
||||||
|
@defproc[(flat-contract-property? [x any/c]) boolean?]
|
||||||
|
)]{
|
||||||
|
These predicates detect whether a value is a @tech{contract property} or a
|
||||||
|
@tech{flat contract property}, respectively.
|
||||||
}
|
}
|
||||||
|
|
||||||
@; ------------------------------------------------------------------------
|
@; ------------------------------------------------------------------------
|
||||||
|
|
||||||
@section{Contract Utilities}
|
@section{Contract Utilities}
|
||||||
|
|
||||||
@defproc[(guilty-party [exn exn?]) any]{
|
|
||||||
|
|
||||||
Extracts the name of the guilty party from an exception
|
|
||||||
raised by the contract system.}
|
|
||||||
|
|
||||||
@defproc[(contract? [v any/c]) boolean?]{
|
@defproc[(contract? [v any/c]) boolean?]{
|
||||||
|
|
||||||
Returns @scheme[#t] if its argument is a contract (i.e., constructed
|
Returns @scheme[#t] if its argument is a contract (i.e., constructed
|
||||||
|
@ -1246,6 +1332,18 @@ may or may not hold. If the contract is a first-order
|
||||||
contract, a result of @scheme[#t] guarantees that the
|
contract, a result of @scheme[#t] guarantees that the
|
||||||
contract holds.}
|
contract holds.}
|
||||||
|
|
||||||
|
@defproc[(contract-name [c contract?]) any/c]{
|
||||||
|
Produces the name used to describe the contract in error messages.
|
||||||
|
}
|
||||||
|
|
||||||
|
@defproc[(contract-first-order [c contract?]) (-> any/c boolean?)]{
|
||||||
|
Produces the first order test used by @scheme[or/c] to match values to higher
|
||||||
|
order contracts.
|
||||||
|
}
|
||||||
|
|
||||||
|
@defproc[(contract-projection [c contract?]) (-> blame? (-> any/c any/c))]{
|
||||||
|
Produces the projection defining a contract's behavior on protected values.
|
||||||
|
}
|
||||||
|
|
||||||
@defproc[(make-none/c [sexp-name any/c]) contract?]{
|
@defproc[(make-none/c [sexp-name any/c]) contract?]{
|
||||||
|
|
||||||
|
@ -1253,31 +1351,22 @@ Makes a contract that accepts no values, and reports the
|
||||||
name @scheme[sexp-name] when signaling a contract violation.}
|
name @scheme[sexp-name] when signaling a contract violation.}
|
||||||
|
|
||||||
|
|
||||||
@defparam[contract-violation->string
|
@defparam[current-blame-format
|
||||||
proc
|
proc
|
||||||
(-> any/c any/c (or/c #f any/c) any/c string? string?)]{
|
(-> blame? any/c string?)]{
|
||||||
|
|
||||||
This is a parameter that is used when constructing a
|
This is a parameter that is used when constructing a
|
||||||
contract violation error. Its value is procedure that
|
contract violation error. Its value is procedure that
|
||||||
accepts five arguments:
|
accepts three arguments:
|
||||||
@itemize[
|
@itemize[
|
||||||
@item{the value that the contract applies to,}
|
@item{the blame object for the violation,}
|
||||||
@item{a syntax object representing the source location where
|
@item{the value that the contract applies to, and}
|
||||||
the contract was established, }
|
@item{a message indicating the kind of violation.}]
|
||||||
@item{the name of the party that violated the contract (@scheme[#f] indicates that the party is not known, not that the party's name is @scheme[#f]), }
|
|
||||||
@item{an sexpression representing the contract, and }
|
|
||||||
@item{a message indicating the kind of violation.
|
|
||||||
}]
|
|
||||||
The procedure then
|
The procedure then
|
||||||
returns a string that is put into the contract error
|
returns a string that is put into the contract error
|
||||||
message. Note that the value is often already included in
|
message. Note that the value is often already included in
|
||||||
the message that indicates the violation.
|
the message that indicates the violation.
|
||||||
|
|
||||||
If the contract was establised via
|
|
||||||
@scheme[provide/contract], the names of the party to the
|
|
||||||
contract will be sexpression versions of the module paths
|
|
||||||
(as returned by @scheme[collapse-module-path]).
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user