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.}
|
@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
|
contract error for this interface. If you can't trust yourself to get
|
||||||
the initial value right, you need to communicate the initial value
|
the initial value right, you need to communicate the initial value
|
||||||
across a boundary.
|
across a boundary.
|
||||||
|
@ -71,7 +71,7 @@ arguments: @racket[char?]. }
|
||||||
|
|
||||||
The @racket[max] operator consumes at least one real number, but it
|
The @racket[max] operator consumes at least one real number, but it
|
||||||
accepts any number of additional arguments. You can write other such
|
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
|
@margin-note{See @secref["rest-args"] for an introduction to rest
|
||||||
arguments.}
|
arguments.}
|
||||||
|
@ -424,7 +424,7 @@ racket
|
||||||
The @racket[->i] contract combinator can also ensure that a
|
The @racket[->i] contract combinator can also ensure that a
|
||||||
function only modifies state according to certain
|
function only modifies state according to certain
|
||||||
constraints. For example, consider this contract
|
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):
|
@racket[preferences:add-panel] in the framework):
|
||||||
@racketblock[
|
@racketblock[
|
||||||
(->i ([parent (is-a?/c area-container-window<%>)])
|
(->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->]
|
The correct contract uses the @racket[unconstrained-domain->]
|
||||||
combinator, which specifies only the range of a function, not its
|
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
|
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[
|
@racketblock[
|
||||||
(provide
|
(provide
|
||||||
(contract-out
|
(contract-out
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
A mathematical function has a @deftech{domain} and a
|
A mathematical function has a @deftech{domain} and a
|
||||||
@deftech{range}. The domain indicates the kind of values that the
|
@deftech{range}. The domain indicates the kind of values that the
|
||||||
function can accept as arguments, and the range indicates the kind of
|
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
|
function with its domain and range is
|
||||||
|
|
||||||
@racketblock[
|
@racketblock[
|
||||||
|
@ -63,7 +63,7 @@ parties is to blame.
|
||||||
|
|
||||||
If a client module were to apply @racket[deposit] to @racket['millions],
|
If a client module were to apply @racket[deposit] to @racket['millions],
|
||||||
it would violate the contract. The contract-monitoring system would
|
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
|
the above module. In contrast, if the @racket[balance] function were
|
||||||
to return @racket['broke], the contract-monitoring system
|
to return @racket['broke], the contract-monitoring system
|
||||||
would blame the server module.
|
would blame the server module.
|
||||||
|
@ -75,7 +75,7 @@ combinator}, which combines other contracts to form a contract.
|
||||||
|
|
||||||
@section{Styles of @racket[->]}
|
@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
|
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
|
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
|
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:
|
In general, each contract error message consists of six sections:
|
||||||
@itemize[@item{a name for the function or method associated with the contract
|
@itemize[@item{a name for the function or method associated with the contract
|
||||||
and either the phrase ``contract violation'' or ``violated it's contract''
|
and either the phrase ``contract violation'' or ``broke its contract''
|
||||||
depending on whether the contract was violated by the server or the
|
depending on whether the contract was violated by the client or the
|
||||||
client; e.g. in the previous example: @lines[0 1]}
|
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 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[2 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[4 1]}
|
@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[5 1]}
|
@item{who was blamed, @lines[6 1]}
|
||||||
@item{and the source location where the contract appears. @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))
|
(define (bst? b) (bst-between? b -inf.0 +inf.0))
|
||||||
|
|
||||||
(provide (struct node (val left right)))
|
(provide (struct-out node))
|
||||||
(provide (contract-out
|
(provide (contract-out
|
||||||
[bst? (any/c . -> . boolean?)]
|
[bst? (any/c . -> . boolean?)]
|
||||||
[in? (number? bst? . -> . 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
|
the tree is at least partially well-formed, but without
|
||||||
changing the complexity.
|
changing the complexity.
|
||||||
|
|
||||||
To do that, we need to use @racket[define-contract-struct] in place of
|
To do that, we need to use @racket[struct/dc] to define
|
||||||
@racket[struct]. Like @racket[struct] (and more like
|
@racket[bst-between?]. Like @racket[struct/c], @racket[struct/dc] defines a
|
||||||
@racket[define-struct]), @racket[define-contract-struct] defines a
|
contract for a structure. Unlike
|
||||||
maker, predicate, and selectors for a new structure. Unlike
|
@racket[struct/c], it allows fields to be marked as lazy, so that
|
||||||
@racket[define-struct], it also defines contract combinators, in this
|
the contracts are only checked when the matching selector is called.
|
||||||
case @racket[node/c] and @racket[node/dc]. Also unlike
|
Also, it does not allow mutable fields to be marked as lazy.
|
||||||
@racket[define-struct], it does not allow mutators, making its structs
|
|
||||||
always immutable.
|
|
||||||
|
|
||||||
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
|
field of the struct and returns a contract on the
|
||||||
struct. More interestingly, the syntactic
|
struct. More interestingly, @racket[struct/dc] allows us to write dependent
|
||||||
form @racket[node/dc] allows us to write dependent
|
|
||||||
contracts, i.e., contracts where some of the contracts on
|
contracts, i.e., contracts where some of the contracts on
|
||||||
the fields depend on the values of other fields. We can use
|
the fields depend on the values of other fields. We can use
|
||||||
this to define the binary search tree contract:
|
this to define the binary search tree contract:
|
||||||
|
@ -215,7 +212,7 @@ this to define the binary search tree contract:
|
||||||
@racketmod[
|
@racketmod[
|
||||||
racket
|
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'")
|
(code:comment "determines if `n' is in the binary search tree `b'")
|
||||||
(define (in? n b) ... as before ...)
|
(define (in? n b) ... as before ...)
|
||||||
|
@ -225,25 +222,28 @@ racket
|
||||||
(code:comment "whose values are between low and high")
|
(code:comment "whose values are between low and high")
|
||||||
(define (bst-between/c low high)
|
(define (bst-between/c low high)
|
||||||
(or/c null?
|
(or/c null?
|
||||||
(node/dc [val (between/c low high)]
|
(struct/dc node [val (between/c low high)]
|
||||||
[left (val) (bst-between/c low val)]
|
[left (val) #:lazy (bst-between/c low val)]
|
||||||
[right (val) (bst-between/c val high)])))
|
[right (val) #:lazy (bst-between/c val high)])))
|
||||||
|
|
||||||
(define bst/c (bst-between/c -inf.0 +inf.0))
|
(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
|
(provide (contract-out
|
||||||
[bst/c contract?]
|
[bst/c contract?]
|
||||||
[in? (number? bst/c . -> . boolean?)]))
|
[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
|
fields and then specify contracts for each field. In the
|
||||||
above, the @racket[val] field is a contract that accepts
|
above, the @racket[val] field is a contract that accepts
|
||||||
values between @racket[low] and @racket[high].
|
values between @racket[low] and @racket[high].
|
||||||
The @racket[left] and @racket[right] fields are
|
The @racket[left] and @racket[right] fields are
|
||||||
dependent on the value of the @racket[val] field,
|
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
|
are built by recursive calls to
|
||||||
the @racket[bst-between/c] function. Taken together,
|
the @racket[bst-between/c] function. Taken together,
|
||||||
this contract ensures the same thing that
|
this contract ensures the same thing that
|
||||||
|
@ -263,8 +263,8 @@ body to be a contract and then optimizes that contract.
|
||||||
@racketblock[
|
@racketblock[
|
||||||
(define-opt/c (bst-between/c low high)
|
(define-opt/c (bst-between/c low high)
|
||||||
(or/c null?
|
(or/c null?
|
||||||
(node/dc [val (between/c low high)]
|
(struct/dc node [val (between/c low high)]
|
||||||
[left (val) (bst-between/c low val)]
|
[left (val) #:lazy (bst-between/c low val)]
|
||||||
[right (val) (bst-between/c val high)])))
|
[right (val) #:lazy (bst-between/c val high)])))
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user