Add initial prose for define/contract in the Guide
This commit adds a few sections that explain some of the subtleties of nested contract boundaries.
This commit is contained in:
parent
f26279bfa2
commit
d37158bd76
|
@ -153,3 +153,39 @@ racket
|
|||
|
||||
Moral: This is a bug that we will address in a future release.
|
||||
|
||||
@ctc-section[#:tag "gotcha-nested"]{Contract boundaries established by @racket[define/contract]}
|
||||
|
||||
The contract boundaries established by @racket[define/contract], which
|
||||
creates a nested contract boundary, is sometimes unintuitive. This is
|
||||
especially true when multiple functions or other values with contracts
|
||||
interact. For example, consider these two interacting functions:
|
||||
|
||||
@(define e2 (make-base-eval))
|
||||
@(interaction-eval #:eval e2 (require racket/contract))
|
||||
@interaction[#:eval e2
|
||||
(define/contract (f x)
|
||||
(-> integer? integer?)
|
||||
x)
|
||||
(define/contract (g)
|
||||
(-> string?)
|
||||
(f "not an integer"))
|
||||
(g)
|
||||
]
|
||||
@(close-eval e2)
|
||||
|
||||
One may expect that the function @racket[g] will be blamed
|
||||
for breaking the terms of its contract with @racket[f]. Instead, the
|
||||
``top-level'' is blamed.
|
||||
|
||||
The reason that the ``top-level'' is blamed is because there are two
|
||||
contract boundaries in play and none of them are directly between
|
||||
@racket[f] and @racket[g]. Both @racket[f] and @racket[g] establish
|
||||
boundaries between themselves and their context, i.e., the top-level.
|
||||
In order for @racket[f] to be called by @racket[g], the arguments
|
||||
that are supplied by @racket[g] pass through the top-level, which
|
||||
takes responsibility for the values ultimately supplied to @racket[f].
|
||||
Thus, it makes sense that the top-level is blamed.
|
||||
|
||||
Moral: if two values with contracts should interact,
|
||||
consider putting them in separate modules with contracts at
|
||||
the module boundary.
|
|
@ -210,3 +210,30 @@ with the @racket[module+] keyword at the front. The first form after
|
|||
@racket[module] is the name of the module to be used in a subsequent
|
||||
@racket[require] statement (where each reference through a
|
||||
@racket[require] prefixes the name with @racket[".."]).
|
||||
|
||||
@ctc-section[#:tag "intro-nested"]{Experimenting with Nested Contract Boundaries}
|
||||
|
||||
In many cases, it makes sense to attach contracts at module boundaries.
|
||||
It is often convenient, however, to be able to use contracts at
|
||||
a finer granularity than modules. The @racket[define/contract]
|
||||
form enables this kind of use:
|
||||
|
||||
@racketmod[
|
||||
racket
|
||||
|
||||
(define/contract amount
|
||||
(and/c number? positive?)
|
||||
150)
|
||||
|
||||
(+ amount 10)
|
||||
]
|
||||
|
||||
In this example, the @racket[define/contract] form establishes a contract
|
||||
boundary between the definition of @racket[amount] and its surrounding
|
||||
context. In other words, the two parties here are the definition and
|
||||
the module that contains it.
|
||||
|
||||
Forms that create these @emph{nested contract boundaries} can sometimes
|
||||
be subtle to use because they may have unexpected performance implications
|
||||
or blame a party that may seem unintuitive. These subtleties are explained
|
||||
in @secref["simple-nested"] and @ctc-link["gotcha-nested"].
|
||||
|
|
|
@ -100,6 +100,36 @@ is just another way of writing
|
|||
(-> number? any)
|
||||
]
|
||||
|
||||
@; ------------------------------------------------------------------------
|
||||
|
||||
@section[#:tag "simple-nested"]{Using @racket[define/contract] and @racket[->]}
|
||||
|
||||
The @racket[define/contract] form introduced in @ctc-link["intro-nested"] can
|
||||
also be used to define functions that come with a contract. For example,
|
||||
|
||||
@racketblock[
|
||||
(define/contract (deposit amount)
|
||||
(-> number? any)
|
||||
(code:comment "implementation goes here")
|
||||
....)
|
||||
]
|
||||
|
||||
which defines the @racket[deposit] function with the contract from earlier.
|
||||
Note that this has two potentially important impacts on the use of
|
||||
@racket[deposit]:
|
||||
|
||||
@itemlist[#:style "ordered"
|
||||
@item{Since the contract will always be checked on calls to @racket[deposit],
|
||||
even inside the module in which it is defined, this may increase
|
||||
the number of times the contract is checked. This could lead to
|
||||
a performance degradation. This is especially true if the function
|
||||
is called repeatedly in loops or recursion.}
|
||||
@item{In some situations, a function may be written to accept a more
|
||||
lax set of inputs when called by other code in the same module.
|
||||
For such use cases, the contract boundary established by
|
||||
@racket[define/contract] is too strict.}
|
||||
]
|
||||
|
||||
@; ----------------------------------------------------------------------
|
||||
@section{@racket[any] and @racket[any/c]}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user