improve the guide section on how to use the contract library
to build new combinators
This commit is contained in:
parent
8bb8365a38
commit
b3d3bf7c01
|
@ -44,7 +44,7 @@ they are not quite ready for use as contracts, because they
|
|||
do not accommodate blame and do not provide good error
|
||||
messages. In order to accommodate these, contracts do not
|
||||
just use simple projections, but use functions that accept a
|
||||
@deftech{blame object} encapsulating
|
||||
@tech[#:doc '(lib "scribblings/reference/reference.scrbl")]{blame object} encapsulating
|
||||
the names of two parties that are the candidates for blame,
|
||||
as well as a record of the source location where the
|
||||
contract was established and the name of the contract. They
|
||||
|
@ -109,7 +109,8 @@ arguments in the first two lines of
|
|||
the @racket[int->int-proj] function. The trick here is that,
|
||||
even though the @racket[int->int-proj] function always
|
||||
blames what it sees as positive, we can swap the blame parties by
|
||||
calling @racket[blame-swap] on the given @tech{blame object}, replacing
|
||||
calling @racket[blame-swap] on the given
|
||||
@tech[#:doc '(lib "scribblings/reference/reference.scrbl")]{blame object}, replacing
|
||||
the positive party with the negative party and vice versa.
|
||||
|
||||
This technique is not merely a cheap trick to get the example to work,
|
||||
|
@ -162,7 +163,8 @@ when a contract violation is detected.
|
|||
While these projections are supported by the contract library
|
||||
and can be used to build new contracts, the contract library
|
||||
also supports a different API for projections that can be more
|
||||
efficient. Specifically, a @deftech{late neg projection} accepts
|
||||
efficient. Specifically, a
|
||||
@tech[#:doc '(lib "scribblings/reference/reference.scrbl")]{late neg projection} accepts
|
||||
a blame object without the negative blame information and then
|
||||
returns a function that accepts both the value to be contracted and
|
||||
the name of the negative party, in that order.
|
||||
|
@ -196,11 +198,55 @@ to use this API looks like this:
|
|||
'(expected "a procedure of one argument" given: "~e")
|
||||
f))))]
|
||||
The advantage of this style of contract is that the @racket[_blame]
|
||||
and @racket[_f] arguments can be supplied on the server side of the
|
||||
contract boundary and the result can be used for every different
|
||||
argument can be supplied on the server side of the
|
||||
contract boundary and the result can be used for each different
|
||||
client. With the simpler situation, a new blame object has to be
|
||||
created for each client.
|
||||
|
||||
One final problem remains before this contract can be used with the
|
||||
rest of the contract system. In the function above,
|
||||
the contract is implemented by creating a wrapper function for
|
||||
@racket[f], but this wrapper function does not cooperate with
|
||||
@racket[equal?], nor does it let the runtime system know that there
|
||||
is a relationship between the result function and @racket[f], the input
|
||||
function.
|
||||
|
||||
To remedy these two problems, we should use
|
||||
@tech[#:doc '(lib "scribblings/reference/reference.scrbl")]{chaperones} instead
|
||||
of just using @racket[λ] to create the wrapper function. Here is the
|
||||
@racket[int->int-proj] function rewritten to use a
|
||||
@tech[#:doc '(lib "scribblings/reference/reference.scrbl")]{chaperone}:
|
||||
|
||||
@interaction/no-prompt[#:eval ex-eval
|
||||
(define (int->int-proj blame)
|
||||
(define dom-blame (blame-add-context blame
|
||||
"the argument of"
|
||||
#:swap? #t))
|
||||
(define rng-blame (blame-add-context blame "the range of"))
|
||||
(define (check-int v to-blame neg-party)
|
||||
(unless (integer? v)
|
||||
(raise-blame-error
|
||||
to-blame #:missing-party neg-party
|
||||
v
|
||||
'(expected "an integer" given: "~e")
|
||||
v)))
|
||||
(λ (f neg-party)
|
||||
(if (and (procedure? f)
|
||||
(procedure-arity-includes? f 1))
|
||||
(chaperone-procedure
|
||||
f
|
||||
(λ (x)
|
||||
(check-int x dom-blame neg-party)
|
||||
(values (λ (ans)
|
||||
(check-int ans rng-blame neg-party)
|
||||
ans)
|
||||
x)))
|
||||
(raise-blame-error
|
||||
blame #:missing-party neg-party
|
||||
f
|
||||
'(expected "a procedure of one argument" given: "~e")
|
||||
f))))]
|
||||
|
||||
Projections like the ones described above, but suited to
|
||||
other, new kinds of value you might make, can be used with
|
||||
the contract library primitives. Specifically, we can use
|
||||
|
@ -470,5 +516,4 @@ to use in this program:
|
|||
(maybe-accepts-a-function sqrt)
|
||||
(maybe-accepts-a-function 123)]
|
||||
|
||||
|
||||
@(close-eval ex-eval)
|
||||
|
|
|
@ -9,10 +9,8 @@
|
|||
(the-eval '(require racket/contract racket/contract/parametric racket/list))
|
||||
the-eval)))
|
||||
|
||||
@(define blame-object
|
||||
@tech[#:doc '(lib "scribblings/guide/guide.scrbl")]{blame object})
|
||||
@(define blame-objects
|
||||
@tech[#:doc '(lib "scribblings/guide/guide.scrbl")]{blame objects})
|
||||
@(define blame-object @tech{blame object})
|
||||
@(define blame-objects @tech{blame objects})
|
||||
|
||||
@title[#:tag "contracts" #:style 'toc]{Contracts}
|
||||
|
||||
|
@ -2182,8 +2180,9 @@ The default test accepts any value. The predicate should be influenced by
|
|||
the value of @racket[(contract-first-order-okay-to-give-up?)] (see it's documentation
|
||||
for more explanation).
|
||||
|
||||
The @racket[late-neg-proj] defines the behavior of applying the contract. If it is
|
||||
supplied, it accepts a blame object that does not have a value for
|
||||
The @racket[late-neg-proj] argument defines the behavior of applying
|
||||
the contract via a @deftech{late neg projection}. If it is
|
||||
supplied, it accepts a @tech{blame object} that does not have a value for
|
||||
the @racket[blame-negative] field. Then it must return a function that accepts
|
||||
both the value that is getting the contract and the name of the blame party, in
|
||||
that order. The result must either be the value (perhaps suitably wrapped
|
||||
|
@ -2360,16 +2359,18 @@ For the costs from checking your new combinator to be included, you should wrap
|
|||
any deferred, higher-order checks with this form. First-order checks are
|
||||
recognized automatically and do not require this form.
|
||||
|
||||
If your combinator's projections operate on complete blame objects (i.e., no
|
||||
missing blame parties), the blame object should be the first argument to this
|
||||
If your combinator's projections operate on complete @tech{blame objects} (i.e., no
|
||||
missing blame parties), the @tech{blame object} should be the first argument to this
|
||||
form. Otherwise (e.g., in the case of @racket[_late-neg] projections), a pair
|
||||
of the blame object and the negative party should be used instead.
|
||||
of the @tech{blame object} and the negative party should be used instead.
|
||||
|
||||
@history[#:added "6.4.0.4"]
|
||||
}
|
||||
|
||||
@subsection{Blame Objects}
|
||||
|
||||
This section describes @deftech{blame objects} and operations on them.
|
||||
|
||||
@defproc[(blame? [v any/c]) boolean?]{
|
||||
This predicate recognizes @|blame-objects|.
|
||||
}
|
||||
|
@ -2520,7 +2521,7 @@ the other; both are provided for convenience and clarity.
|
|||
@defproc[(blame-add-missing-party [b (and/c blame? blame-missing-party?)]
|
||||
[missing-party any/c])
|
||||
(and/c blame? (not/c blame-missing-party?))]{
|
||||
Produces a new blame object like @racket[b], except that the missing
|
||||
Produces a new @tech{blame object} like @racket[b], except that the missing
|
||||
party is replaced with @racket[missing-party].
|
||||
}
|
||||
|
||||
|
@ -2619,8 +2620,8 @@ The value is expected to be the blame record for the contract on the value or
|
|||
a @racket[cons]-pair of a blame record with a missing party and the missing
|
||||
party. The @racket[value-blame] function reassembles the arguments of the pair
|
||||
into a complete blame record using @racket[blame-add-missing-party]. If
|
||||
the value has one of the properties, but the value is not a blame object
|
||||
or a pair whose @racket[car] position is a blame object, then @racket[has-blame?]
|
||||
the value has one of the properties, but the value is not a @tech{blame object}
|
||||
or a pair whose @racket[car] position is a @tech{blame object}, then @racket[has-blame?]
|
||||
returns @racket[#f] but @racket[value-blame] returns @racket[#f].
|
||||
}
|
||||
|
||||
|
@ -3080,7 +3081,7 @@ Produces the name used to describe the contract in error messages.
|
|||
the contract checking, mostly used to create a meaningful error message if
|
||||
a contract violation is detected. The resulting function's first argument
|
||||
is the value that should have the contract and its second argument is
|
||||
a ``missing party'' from the blame object, to be passed to @racket[raise-contract-error].
|
||||
a ``missing party'' from the @tech{blame object}, to be passed to @racket[raise-contract-error].
|
||||
|
||||
If possible, use this function instead of @racket[contract-val-first-projection] or
|
||||
@racket[contract-projection].
|
||||
|
|
Loading…
Reference in New Issue
Block a user