Improve Guide chapter 7
* Typos/grammar * Fix breakdown of contract error message * Show `struct/dc` instead of deprecated lazy contracts
This commit is contained in:
parent
14c77c39d2
commit
ef4b3feb8d
|
@ -62,7 +62,7 @@ arguments: @racket[char?]. }
|
|||
@item{The last one is a single contract: the result of the function.}
|
||||
]
|
||||
|
||||
Note if a default value does not satisfy a contract, you won't get a
|
||||
Note that if a default value does not satisfy a contract, you won't get a
|
||||
contract error for this interface. If you can't trust yourself to get
|
||||
the initial value right, you need to communicate the initial value
|
||||
across a boundary.
|
||||
|
@ -71,7 +71,7 @@ arguments: @racket[char?]. }
|
|||
|
||||
The @racket[max] operator consumes at least one real number, but it
|
||||
accepts any number of additional arguments. You can write other such
|
||||
functions using a ``rest'' argument, such as in @racket[max-abs]:
|
||||
functions using a @tech{rest argument}, such as in @racket[max-abs]:
|
||||
|
||||
@margin-note{See @secref["rest-args"] for an introduction to rest
|
||||
arguments.}
|
||||
|
@ -424,7 +424,7 @@ racket
|
|||
The @racket[->i] contract combinator can also ensure that a
|
||||
function only modifies state according to certain
|
||||
constraints. For example, consider this contract
|
||||
(it is a slightly simplified from the function
|
||||
(it is a slightly simplified version from the function
|
||||
@racket[preferences:add-panel] in the framework):
|
||||
@racketblock[
|
||||
(->i ([parent (is-a?/c area-container-window<%>)])
|
||||
|
@ -614,7 +614,7 @@ because the given function accepts only one argument.
|
|||
The correct contract uses the @racket[unconstrained-domain->]
|
||||
combinator, which specifies only the range of a function, not its
|
||||
domain. It is then possible to combine this contract with an arity test to
|
||||
specify the correct @racket[n-step]'s contract:
|
||||
specify the correct contract for @racket[n-step]:
|
||||
@racketblock[
|
||||
(provide
|
||||
(contract-out
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
A mathematical function has a @deftech{domain} and a
|
||||
@deftech{range}. The domain indicates the kind of values that the
|
||||
function can accept as arguments, and the range indicates the kind of
|
||||
values that it produces. The conventional notation for a describing a
|
||||
values that it produces. The conventional notation for describing a
|
||||
function with its domain and range is
|
||||
|
||||
@racketblock[
|
||||
|
@ -63,7 +63,7 @@ parties is to blame.
|
|||
|
||||
If a client module were to apply @racket[deposit] to @racket['millions],
|
||||
it would violate the contract. The contract-monitoring system would
|
||||
catch this violation and blame client for breaking the contract with
|
||||
catch this violation and blame the client for breaking the contract with
|
||||
the above module. In contrast, if the @racket[balance] function were
|
||||
to return @racket['broke], the contract-monitoring system
|
||||
would blame the server module.
|
||||
|
@ -75,7 +75,7 @@ combinator}, which combines other contracts to form a contract.
|
|||
|
||||
@section{Styles of @racket[->]}
|
||||
|
||||
If you are used to mathematical function, you may prefer a contract
|
||||
If you are used to mathematical functions, you may prefer a contract
|
||||
arrow to appear between the domain and the range of a function, not
|
||||
at the beginning. If you have read @|HtDP|, you have seen this many
|
||||
times. Indeed, you may have seen contracts such as these in other
|
||||
|
@ -410,11 +410,11 @@ With this little change, the error message becomes quite readable:
|
|||
|
||||
In general, each contract error message consists of six sections:
|
||||
@itemize[@item{a name for the function or method associated with the contract
|
||||
and either the phrase ``contract violation'' or ``violated it's contract''
|
||||
depending on whether the contract was violated by the server or the
|
||||
client; e.g. in the previous example: @lines[0 1]}
|
||||
@item{a description of the precise aspect of the contract that was violated, @lines[1 1]}
|
||||
@item{the complete contract plus a path into it showing which aspect was violated, @lines[2 2]}
|
||||
@item{the module where the contract was put (or, more generally, the boundary that the contract mediates), @lines[4 1]}
|
||||
@item{who was blamed, @lines[5 1]}
|
||||
@item{and the source location where the contract appears. @lines[6 1]}]
|
||||
and either the phrase ``contract violation'' or ``broke its contract''
|
||||
depending on whether the contract was violated by the client or the
|
||||
server; e.g. in the previous example: @lines[0 1]}
|
||||
@item{a description of the precise aspect of the contract that was violated, @lines[1 2]}
|
||||
@item{the complete contract plus a path into it showing which aspect was violated, @lines[3 2]}
|
||||
@item{the module where the contract was put (or, more generally, the boundary that the contract mediates), @lines[5 1]}
|
||||
@item{who was blamed, @lines[6 1]}
|
||||
@item{and the source location where the contract appears. @lines[7 1]}]
|
||||
|
|
|
@ -166,7 +166,7 @@ racket
|
|||
|
||||
(define (bst? b) (bst-between? b -inf.0 +inf.0))
|
||||
|
||||
(provide (struct node (val left right)))
|
||||
(provide (struct-out node))
|
||||
(provide (contract-out
|
||||
[bst? (any/c . -> . boolean?)]
|
||||
[in? (number? bst? . -> . boolean?)]))
|
||||
|
@ -195,19 +195,16 @@ that @racket[in?] looks at, we can still guarantee that
|
|||
the tree is at least partially well-formed, but without
|
||||
changing the complexity.
|
||||
|
||||
To do that, we need to use @racket[define-contract-struct] in place of
|
||||
@racket[struct]. Like @racket[struct] (and more like
|
||||
@racket[define-struct]), @racket[define-contract-struct] defines a
|
||||
maker, predicate, and selectors for a new structure. Unlike
|
||||
@racket[define-struct], it also defines contract combinators, in this
|
||||
case @racket[node/c] and @racket[node/dc]. Also unlike
|
||||
@racket[define-struct], it does not allow mutators, making its structs
|
||||
always immutable.
|
||||
To do that, we need to use @racket[struct/dc] to define
|
||||
@racket[bst-between?]. Like @racket[struct/c], @racket[struct/dc] defines a
|
||||
contract for a structure. Unlike
|
||||
@racket[struct/c], it allows fields to be marked as lazy, so that
|
||||
the contracts are only checked when the matching selector is called.
|
||||
Also, it does not allow mutable fields to be marked as lazy.
|
||||
|
||||
The @racket[node/c] function accepts a contract for each
|
||||
The @racket[struct/dc] form accepts a contract for each
|
||||
field of the struct and returns a contract on the
|
||||
struct. More interestingly, the syntactic
|
||||
form @racket[node/dc] allows us to write dependent
|
||||
struct. More interestingly, @racket[struct/dc] allows us to write dependent
|
||||
contracts, i.e., contracts where some of the contracts on
|
||||
the fields depend on the values of other fields. We can use
|
||||
this to define the binary search tree contract:
|
||||
|
@ -215,7 +212,7 @@ this to define the binary search tree contract:
|
|||
@racketmod[
|
||||
racket
|
||||
|
||||
(define-contract-struct node (val left right))
|
||||
(struct node (val left right))
|
||||
|
||||
(code:comment "determines if `n' is in the binary search tree `b'")
|
||||
(define (in? n b) ... as before ...)
|
||||
|
@ -225,25 +222,28 @@ racket
|
|||
(code:comment "whose values are between low and high")
|
||||
(define (bst-between/c low high)
|
||||
(or/c null?
|
||||
(node/dc [val (between/c low high)]
|
||||
[left (val) (bst-between/c low val)]
|
||||
[right (val) (bst-between/c val high)])))
|
||||
(struct/dc node [val (between/c low high)]
|
||||
[left (val) #:lazy (bst-between/c low val)]
|
||||
[right (val) #:lazy (bst-between/c val high)])))
|
||||
|
||||
(define bst/c (bst-between/c -inf.0 +inf.0))
|
||||
|
||||
(provide make-node node-left node-right node-val node?)
|
||||
(provide (struct-out node))
|
||||
(provide (contract-out
|
||||
[bst/c contract?]
|
||||
[in? (number? bst/c . -> . boolean?)]))
|
||||
]
|
||||
|
||||
In general, each use of @racket[node/dc] must name the
|
||||
In general, each use of @racket[struct/dc] must name the
|
||||
fields and then specify contracts for each field. In the
|
||||
above, the @racket[val] field is a contract that accepts
|
||||
values between @racket[low] and @racket[high].
|
||||
The @racket[left] and @racket[right] fields are
|
||||
dependent on the value of the @racket[val] field,
|
||||
indicated by their second sub-expressions. Their contracts
|
||||
indicated by their second sub-expressions. They are
|
||||
also marked with the @racket[#:lazy] keyword to indicate
|
||||
that they should be checked only when the appropriate
|
||||
accessor is called on the struct instance. Their contracts
|
||||
are built by recursive calls to
|
||||
the @racket[bst-between/c] function. Taken together,
|
||||
this contract ensures the same thing that
|
||||
|
@ -263,8 +263,8 @@ body to be a contract and then optimizes that contract.
|
|||
@racketblock[
|
||||
(define-opt/c (bst-between/c low high)
|
||||
(or/c null?
|
||||
(node/dc [val (between/c low high)]
|
||||
[left (val) (bst-between/c low val)]
|
||||
[right (val) (bst-between/c val high)])))
|
||||
(struct/dc node [val (between/c low high)]
|
||||
[left (val) #:lazy (bst-between/c low val)]
|
||||
[right (val) #:lazy (bst-between/c val high)])))
|
||||
]
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user