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:
Asumu Takikawa 2013-03-01 05:15:04 -05:00
parent 14c77c39d2
commit ef4b3feb8d
3 changed files with 40 additions and 40 deletions

View File

@ -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

View File

@ -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]}]

View File

@ -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)])))
] ]