more rackty guide, through the contracts section
This commit is contained in:
parent
7ed0d4e00a
commit
2f5b166073
|
@ -6,7 +6,7 @@
|
|||
(for-label racket/contract)
|
||||
(for-label racket/gui))
|
||||
|
||||
@title[#:tag "contracts-examples"]{Examples}
|
||||
@title[#:tag "contracts-examples"]{Additional Examples}
|
||||
|
||||
This section illustrates the current state of Racket's contract
|
||||
implementation with a series of examples from @italic{Design by
|
||||
|
@ -59,7 +59,7 @@ Note: To mimic Mitchell and McKim's informal notion of parametericity
|
|||
places, this use of first-class contracts improves on Mitchell and McKim's
|
||||
design (see comments in interfaces).
|
||||
|
||||
@section{A Customer Manager Component for Managing Customer Relationships}
|
||||
@section{A Customer-Manager Component}
|
||||
|
||||
This first module contains some struct definitions in a
|
||||
separate module in order to better track bugs.
|
||||
|
|
|
@ -3,29 +3,29 @@
|
|||
scribble/eval
|
||||
"guide-utils.ss"
|
||||
"contracts-utils.ss"
|
||||
(for-label scheme/contract))
|
||||
(for-label racket/contract))
|
||||
|
||||
@title[#:tag "contracts-exists"]{Abstract Contracts using @scheme[#:exists] and @scheme[#:∃]}
|
||||
@title[#:tag "contracts-exists"]{Abstract Contracts using @racket[#:exists] and @racket[#:∃]}
|
||||
|
||||
The contract system provides existential contracts that can
|
||||
protect abstractions, ensuring that clients of your module
|
||||
cannot depend on the precise representation choices you make
|
||||
for your data structures.
|
||||
|
||||
@ctc-section{Getting Started, with a Stack Example}
|
||||
#; @ctc-section{Getting Started, with a Stack Example}
|
||||
|
||||
@margin-note{
|
||||
You can type @scheme[#:exists] instead of @scheme[#:∃] if you
|
||||
cannot easily type unicode characters; in DrScheme, typing
|
||||
You can type @racket[#:exists] instead of @racket[#:∃] if you
|
||||
cannot easily type unicode characters; in DrRacket, typing
|
||||
@tt{\exists} followed by either alt-\ or control-\ (depending
|
||||
on your platform) will produce @scheme[∃].}
|
||||
The @scheme[provide/contract] form allows you to write
|
||||
@schemeblock[#:∃ _name-of-a-new-contract] as one of its clauses. This declaration
|
||||
introduces the variable @scheme[_name-of-a-new-contract], binding it to a new
|
||||
on your platform) will produce @racket[∃].}
|
||||
The @racket[provide/contract] form allows you to write
|
||||
@racketblock[#:∃ _name-of-a-new-contract] as one of its clauses. This declaration
|
||||
introduces the variable @racket[_name-of-a-new-contract], binding it to a new
|
||||
contract that hides information about the values it protects.
|
||||
|
||||
As an example, consider this (simple) implementation of a stack datastructure:
|
||||
@schememod[scheme
|
||||
@racketmod[racket
|
||||
(define empty '())
|
||||
(define (enq top queue) (append queue (list top)))
|
||||
(define (next queue) (car queue))
|
||||
|
@ -39,14 +39,14 @@ As an example, consider this (simple) implementation of a stack datastructure:
|
|||
[deq (-> (listof integer?) (listof integer?))]
|
||||
[empty? (-> (listof integer?) boolean?)])]
|
||||
This code implements a queue purely in terms of lists, meaning that clients
|
||||
of this data structure might use @scheme[car] and @scheme[cdr] directly on the
|
||||
of this data structure might use @racket[car] and @racket[cdr] directly on the
|
||||
data structure (perhaps accidentally) and thus any change in the representation
|
||||
(say to a more efficient representation that supports amortized constant time
|
||||
enqueue and dequeue operations) might break client code.
|
||||
|
||||
To ensure that the stack representation is abstact, we can use @scheme[#:∃] in the
|
||||
@scheme[provide/contract] expression, like this:
|
||||
@schemeblock[(provide/contract
|
||||
To ensure that the stack representation is abstact, we can use @racket[#:∃] in the
|
||||
@racket[provide/contract] expression, like this:
|
||||
@racketblock[(provide/contract
|
||||
#:∃ stack
|
||||
[empty stack]
|
||||
[enq (-> integer? stack stack)]
|
||||
|
@ -54,7 +54,7 @@ To ensure that the stack representation is abstact, we can use @scheme[#:∃] in
|
|||
[deq (-> stack (listof integer?))]
|
||||
[empty? (-> stack boolean?)])]
|
||||
|
||||
Now, if clients of the data structure try to use @scheme[car] and @scheme[cdr], they
|
||||
Now, if clients of the data structure try to use @racket[car] and @racket[cdr], they
|
||||
receive an error, rather than mucking about with the internals of the queues.
|
||||
|
||||
See also @ctc-link["exists-gotcha"].
|
||||
|
|
|
@ -9,66 +9,15 @@
|
|||
|
||||
@title[#:tag "contracts-general-functions"]{Contracts on Functions in General}
|
||||
|
||||
@ctc-section[#:tag "flat-named-contracts"]{Contract Error Messages that Contain ``???''}
|
||||
|
||||
You wrote your module. You added contracts. You put them into the interface
|
||||
so that client programmers have all the information from interfaces. It's a
|
||||
piece of art:
|
||||
@racketmod[
|
||||
racket
|
||||
|
||||
(provide/contract
|
||||
[deposit (-> (lambda (x)
|
||||
(and (number? x) (integer? x) (>= x 0)))
|
||||
any)])
|
||||
|
||||
(define this 0)
|
||||
(define (deposit a) ...)
|
||||
]
|
||||
|
||||
Several clients used your module. Others used their
|
||||
modules in turn. And all of a sudden one of them sees this error
|
||||
message:
|
||||
|
||||
@inset-flow{@racketerror{bank-client broke the contract (-> ??? any)
|
||||
it had with myaccount on deposit; expected <???>, given: -10}}
|
||||
|
||||
Clearly, @racket[bank-client] is a module that uses @racket[myaccount]
|
||||
but what is the @racketerror{???} doing there? Wouldn't it be nice if
|
||||
we had a name for this class of data much like we have string, number,
|
||||
and so on?
|
||||
|
||||
For this situation, Racket provides @deftech{flat named
|
||||
contracts}. The use of ``contract'' in this term shows that contracts
|
||||
are first-class values. The ``flat'' means that the collection of data
|
||||
is a subset of the built-in atomic classes of data; they are described
|
||||
by a predicate that consumes all Racket values and produces a
|
||||
boolean. The ``named'' part says what we want to do, which is to name
|
||||
the contract so that error messages become intelligible:
|
||||
|
||||
@racketmod[
|
||||
racket
|
||||
|
||||
(define (amount? x) (and (number? x) (integer? x) (>= x 0)))
|
||||
(define amount (flat-named-contract 'amount amount?))
|
||||
|
||||
(provide/contract
|
||||
[deposit (amount . -> . any)])
|
||||
|
||||
(define this 0)
|
||||
(define (deposit a) ...)
|
||||
]
|
||||
|
||||
With this little change, the error message becomes all of the
|
||||
sudden quite readable:
|
||||
|
||||
@inset-flow{@racketerror{bank-client broke the contract (-> amount
|
||||
any) it had with myaccount on deposit; expected <amount>, given: -10}}
|
||||
The @racket[->] contract constructor works for functions that take a
|
||||
fixed number of arguments and where the result contract is independent
|
||||
of the input arguments. To support other kinds of functions, Racket
|
||||
supplies additional contract constructors, notable @racket[->].
|
||||
|
||||
@ctc-section[#:tag "optional"]{Optional Arguments}
|
||||
|
||||
Take a look at this excerpt from a string-processing module, inspired by the
|
||||
@link["http://racketcookbook.org"]{Racket cookbook}:
|
||||
@link["http://schemecookbook.org"]{Scheme cookbook}:
|
||||
|
||||
@racketmod[
|
||||
racket
|
||||
|
@ -104,6 +53,7 @@ point here is the formulation of the contract for the
|
|||
|
||||
|
||||
The contract combinator @racket[->*], demands several groups of contracts:
|
||||
|
||||
@itemize[
|
||||
@item{The first one is a parenthesized group of contracts for all required
|
||||
arguments. In this example, we see two: @racket[string?] and
|
||||
|
@ -115,64 +65,57 @@ 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 contract error for this interface. In contrast
|
||||
to type systems, we do trust you; if you can't trust
|
||||
yourself, you need to communicate across boundaries for
|
||||
everything you write.
|
||||
Note 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.
|
||||
|
||||
@ctc-section[#:tag "rest-args"]{Rest Arguments}
|
||||
|
||||
We all know that @racket[+] in Beginner Racket is a function
|
||||
that consumes at least two numbers but, in principle,
|
||||
arbitrarily many more. Defining the function is easy:
|
||||
@racketblock[
|
||||
(define (plus fst snd . rst)
|
||||
(foldr + (+ fst snd) rst))
|
||||
]
|
||||
Describing this function via a contract is difficult because of the rest
|
||||
argument (@racket[rst]).
|
||||
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]:
|
||||
|
||||
@margin-note{See @secref["rest-args"] for an introduction to rest
|
||||
arguments.}
|
||||
|
||||
@racketblock[
|
||||
(define (max-abs n . rst)
|
||||
(foldr (lambda (n m) (max (abs n) m)) (abs n) rst))
|
||||
]
|
||||
|
||||
Describing this function through a contract requires a further
|
||||
extension of @racket[->*]: a @racket[#:rest] keyword specifies a
|
||||
contract on a list of arguments after the required and optional
|
||||
arguments:
|
||||
|
||||
Here is the contract:
|
||||
@racketblock[
|
||||
(provide/contract
|
||||
[plus (->* (number? number?) () #:rest (listof number?) number?)])
|
||||
]
|
||||
The @racket[->*] contract combinator empowers you to specify
|
||||
functions that consume a variable number of arguments or functions like
|
||||
@racket[plus], which consume ``at least this number'' of arguments but
|
||||
an arbitrary number of additional arguments.
|
||||
|
||||
The contracts for the required arguments are enclosed in the first
|
||||
pair of parentheses:
|
||||
@racketblock[
|
||||
(number? number?)
|
||||
[max-abs (->* (real?) () #:rest (listof real?) real?)])
|
||||
]
|
||||
|
||||
For @racket[plus] they demand two numbers. The empty pair of
|
||||
parenthesis indicates that there are no optional arguments
|
||||
(not counting the rest arguments) and the contract for the
|
||||
rest argument follows @racket[#:rest]
|
||||
@racketblock[
|
||||
(listof number?)
|
||||
]
|
||||
Since the remainder of the actual arguments are collected
|
||||
in a list for a rest parameter such as @racket[rst], the
|
||||
contract demands a list of values; in this specific
|
||||
examples, these values must be numbers.
|
||||
As always for @racket[->*], the contracts for the required arguments
|
||||
are enclosed in the first pair of parentheses, which in this case is a
|
||||
single real number. The empty pair of parenthesis indicates that there
|
||||
are no optional arguments (not counting the rest arguments). The
|
||||
contract for the rest argument follows @racket[#:rest]; since all
|
||||
additional arguments must be real numbers, the list of rest arguments
|
||||
must satisfy the contract @racket[(listof real?)].
|
||||
|
||||
|
||||
@ctc-section[#:tag "keywords"]{Keyword Arguments}
|
||||
|
||||
Sometimes, a function accepts many arguments and remembering
|
||||
their order can be a nightmare. To help with such functions,
|
||||
Racket has @seclink["lambda-keywords"]{keyword} arguments.
|
||||
It turns out that the @racket[->] contract constructor also contains
|
||||
support for keyword arguments. For example, consider this function,
|
||||
which creates a simple GUI and asks the user a yes-or-no question:
|
||||
|
||||
@margin-note{See @secref["lambda-keywords"] for an introduction to
|
||||
keyword arguments.}
|
||||
|
||||
For example, consider this function that creates a simple
|
||||
GUI and asks the user a yes-or-no question:
|
||||
@racketmod[
|
||||
racket/gui
|
||||
|
||||
(define (ask-yes-or-no-question #:question question
|
||||
(define (ask-yes-or-no-question question
|
||||
#:default answer
|
||||
#:title title
|
||||
#:width w
|
||||
|
@ -194,41 +137,43 @@ racket/gui
|
|||
|
||||
(provide/contract
|
||||
[ask-yes-or-no-question
|
||||
(-> #:question string?
|
||||
(-> string?
|
||||
#:default boolean?
|
||||
#:title string?
|
||||
#:width exact-integer?
|
||||
#:height exact-integer?
|
||||
boolean?)])
|
||||
]
|
||||
@margin-note{Note that if you really want to ask a yes-or-no
|
||||
question via a GUI, you should use
|
||||
@racket[message-box/custom] (and generally speaking,
|
||||
avoiding the responses ``yes'' and ``no'' in your dialog is a
|
||||
good idea, too ...).}
|
||||
|
||||
The contract for @racket[ask-yes-or-no-question] uses our
|
||||
old friend the @racket[->] contract combinator. Just like
|
||||
@racket[lambda] (or @racket[define]-based functions) use
|
||||
keywords for specifying keyword arguments, it uses keywords
|
||||
for specifying contracts on keyword arguments. In this case,
|
||||
it says that @racket[ask-yes-or-no-question] must receive
|
||||
five keyword arguments, one for each of the keywords
|
||||
@racket[#:question],
|
||||
@margin-note{If you really want to ask a yes-or-no question
|
||||
via a GUI, you should use @racket[message-box/custom]. For that
|
||||
matter, it's usually better to provide buttons with more specific
|
||||
answers than ``yes'' and ``no.''}
|
||||
|
||||
The contract for @racket[ask-yes-or-no-question] uses @racket[->], and
|
||||
in the same way that @racket[lambda] (or @racket[define]-based
|
||||
functions) allows a keyword to precede a functions formal argument,
|
||||
@racket[->] allows a keyword to precede a function contract's argument
|
||||
contract. In this case,
|
||||
the contract says that @racket[ask-yes-or-no-question] must receive four keyword
|
||||
arguments, one for each of the keywords
|
||||
@racket[#:default],
|
||||
@racket[#:title],
|
||||
@racket[#:width], and
|
||||
@racket[#:height].
|
||||
Also, just like in a function definition, the keywords in
|
||||
the @racket[->] may appear in any order.
|
||||
As in a function definition, the order of the keywords in @racket[->]
|
||||
relative to each other does not matter for clients of the function;
|
||||
only the relative order of argument contracts without keywords
|
||||
matters.
|
||||
|
||||
@ctc-section[#:tag "optional-keywords"]{Optional Keyword Arguments}
|
||||
|
||||
Of course, many of the parameters in
|
||||
@racket[ask-yes-or-no-question] (from the previous question)
|
||||
have reasonable defaults, and should be made optional:
|
||||
have reasonable defaults and should be made optional:
|
||||
|
||||
@racketblock[
|
||||
(define (ask-yes-or-no-question #:question question
|
||||
(define (ask-yes-or-no-question question
|
||||
#:default answer
|
||||
#:title [title "Yes or No?"]
|
||||
#:width [w 400]
|
||||
|
@ -237,17 +182,18 @@ have reasonable defaults, and should be made optional:
|
|||
]
|
||||
|
||||
To specify this function's contract, we need to use
|
||||
@racket[->*]. It too supports keywords just as you might
|
||||
expect, in both the optional and mandatory argument
|
||||
sections. In this case, we have mandatory keywords
|
||||
@racket[#:question] and @racket[#:default], and optional keywords
|
||||
@racket[->*] again. It supports keywords just as you might
|
||||
expect in both the optional and mandatory argument
|
||||
sections. In this case, we have the mandatory keyword
|
||||
@racket[#:default] and optional keywords
|
||||
@racket[#:title],
|
||||
@racket[#:width], and
|
||||
@racket[#:height]. So, we write the contract like this:
|
||||
|
||||
@racketblock[
|
||||
(provide/contract
|
||||
[ask-yes-or-no-question
|
||||
(->* (#:question string?
|
||||
(->* (string?
|
||||
#:default boolean?)
|
||||
|
||||
(#:title string?
|
||||
|
@ -256,71 +202,182 @@ sections. In this case, we have mandatory keywords
|
|||
|
||||
boolean?)])
|
||||
]
|
||||
putting the mandatory keywords in the first section and the
|
||||
optional ones in the second section.
|
||||
|
||||
@ctc-section[#:tag "arrow-d"]{When a Function's Result Depends on its Arguments}
|
||||
That is, we put the mandatory keywords in the first section, and we
|
||||
put the optional ones in the second section.
|
||||
|
||||
Here is an excerpt from an imaginary (pardon the pun) numerics module:
|
||||
|
||||
@racketmod[
|
||||
racket
|
||||
(provide/contract
|
||||
[sqrt.v1 (->d ([argument (>=/c 1)])
|
||||
()
|
||||
[result (<=/c argument)])])
|
||||
...
|
||||
@ctc-section[#:tag "case-lambda"]{Contracts for @racket[case-lambda]}
|
||||
|
||||
A function defined with @racket[case-lambda] might impose different
|
||||
constraints on its arguments depending on how many are provided. For
|
||||
example, a @racket[report-cost] function might convert either a pair
|
||||
or numbers or a string into a new string:
|
||||
|
||||
@margin-note{See @secref["case-lambda"] for an introduction to
|
||||
@racket[case-lambda].}
|
||||
|
||||
@def+int[
|
||||
(define report-cost
|
||||
(case-lambda
|
||||
[(lo hi) (format "between $~a and $~a" lo hi)]
|
||||
[(desc) (format "~a of dollars" desc)]))
|
||||
(report-cost 5 8)
|
||||
(report-cost "millions")
|
||||
]
|
||||
|
||||
The contract for the exported function @racket[sqrt.v1] uses the
|
||||
@racket[->d] rather than @racket[->] function contract. The ``d''
|
||||
stands for @italic{dependent} contract, meaning the contract for the
|
||||
function range depends on the value of the argument.
|
||||
The contract for such a function is formed with the @racket[case->]
|
||||
combinator, which combines as many functional contracts as needed:
|
||||
@racketblock[
|
||||
(provide/contract
|
||||
[report-cost
|
||||
(case->
|
||||
(integer? integer? . -> . string?)
|
||||
(string? . -> . string?))])
|
||||
]
|
||||
As you can see, the contract for @racket[report-cost] combines two
|
||||
function contracts, which is just as many clauses as the explanation
|
||||
of its functionality required.
|
||||
|
||||
In this particular case, the argument of @racket[sqrt.v1] is greater
|
||||
or equal to 1. Hence a very basic correctness check is that the result is
|
||||
smaller than the argument. (Naturally, if this function is critical, one
|
||||
could strengthen this check with additional clauses.)
|
||||
@;{
|
||||
This isn't supported anymore (yet...?). -robby
|
||||
|
||||
In the case of @racket[substring1], we also know that the indices
|
||||
that it consumes ought to be natural numbers less than the length of the
|
||||
given string. Since @racket[case->] just combines arrow contracts,
|
||||
adding such constraints is just a matter of strengthening the individual
|
||||
contracts:
|
||||
<racket>
|
||||
(provide/contract
|
||||
[substring1 (case->
|
||||
(string? . -> . string?)
|
||||
(->r ([s string?]
|
||||
[_ (and/c natural-number/c (</c (string-length s)))])
|
||||
string?)
|
||||
(->r ([s string?]
|
||||
[a (and/c natural-number/c (</c (string-length s)))]
|
||||
[o (and/c natural-number/c
|
||||
(>=/c a)
|
||||
(</c (string-length s)))])
|
||||
string?))])
|
||||
</racket>
|
||||
Here we used @racket[->r] to name the parameters and express the
|
||||
numeric constraints on them.
|
||||
}
|
||||
|
||||
@ctc-section[#:tag "arrow-d"]{Argument and Result Dependencies}
|
||||
|
||||
The following is an excerpt from an imaginary numerics module:
|
||||
|
||||
@racketblock[
|
||||
(provide/contract
|
||||
[real-sqrt (->d ([argument (>=/c 1)])
|
||||
()
|
||||
[result (<=/c argument)])])
|
||||
]
|
||||
|
||||
The contract for the exported function @racket[real-sqrt] uses the
|
||||
@racket[->d] rather than @racket[->*] function contract. The ``d''
|
||||
stands for a @italic{dependent} contract, meaning the contract for the
|
||||
function range depends on the value of the argument. In this
|
||||
particular case, the argument of @racket[real-sqrt] is greater or
|
||||
equal to 1, so a very basic correctness check is that the result is
|
||||
smaller than the argument.
|
||||
|
||||
In general, a dependent function contract looks just like
|
||||
the more general @racket[->*] contract, but with names added
|
||||
that can be used elsewhere in the contract.
|
||||
|
||||
@;{
|
||||
Yes, there are many other contract combinators such as @racket[<=/c]
|
||||
and @racket[>=/c], and it pays off to look them up in the contract
|
||||
section of the reference manual. They simplify contracts tremendously
|
||||
and make them more accessible to potential clients.
|
||||
}
|
||||
|
||||
@ctc-section[#:tag "arrow-d-args"]{When Contract Arguments Depend on Each Other}
|
||||
Going back to the back-account example, suppose that we generalize the
|
||||
module to support multiple accounts and that we also include a
|
||||
withdrawal operation. The improved bank-account module includes a
|
||||
@racket[account] structure type and the following functions:
|
||||
|
||||
Eventually bank customers want their money back. Hence, a module that
|
||||
implements a bank account must include a method for withdrawing money. Of
|
||||
course, ordinary accounts don't let customers withdraw an arbitrary amount of
|
||||
money but only as much as they have in the account.
|
||||
|
||||
Suppose the account module provides the following two functions:
|
||||
@racketblock[
|
||||
balance : (-> account amount)
|
||||
withdraw : (-> account amount account)
|
||||
(provide/contract
|
||||
[balance (-> account? amount/c)]
|
||||
[withdraw (-> account? amount/c account?)]
|
||||
[deposit (-> account? amount/c account?)])
|
||||
]
|
||||
Then, informally, the proper precondition for @racket[withdraw] is that
|
||||
``the balance of the given account is greater than or equal to the given (withdrawal) amount.''
|
||||
The postcondition is similar to the one for
|
||||
@ctc-link["flat-named-contracts"]{@racket[deposit]}:
|
||||
``the balance of the resulting account is larger than (or equal to) the one of the
|
||||
given account.''
|
||||
You could of course also formulate a full-fledged correctness condition, namely,
|
||||
that the balance of the resulting account is equal to the balance of the given
|
||||
one, plus the given amount.
|
||||
|
||||
The following module implements accounts imperatively and specifies the
|
||||
conditions we just discussed:
|
||||
Besides requiring that a client provide a valid amount for a
|
||||
withdrawal, however, the amount should be less than the specified
|
||||
account's balance, and the resulting account will have less money than
|
||||
it started with. Similarly, the module might promise that a deposit
|
||||
produces an account with money added to the account. The following
|
||||
implementation enforces those constraints and guarantees through
|
||||
contracts:
|
||||
|
||||
@racketmod[
|
||||
racket
|
||||
|
||||
(code:comment "section 1: the contract definitions")
|
||||
(define-struct account (balance) #:mutable)
|
||||
(define amount natural-number/c)
|
||||
(struct account (balance))
|
||||
(define amount/c natural-number/c)
|
||||
|
||||
(code:comment "section 2: the exports")
|
||||
(provide/contract
|
||||
[create (amount/c . -> . account?)]
|
||||
[balance (account? . -> . amount/c)]
|
||||
[withdraw (->d ([acc account?]
|
||||
[amt (and/c amount/c (<=/c (balance acc)))])
|
||||
()
|
||||
[result (and/c account?
|
||||
(lambda (res)
|
||||
(>= (balance res)
|
||||
(- (balance acc) amt))))])]
|
||||
[deposit (->d ([acc account?]
|
||||
[amt amount/c])
|
||||
()
|
||||
[result (and/c account?
|
||||
(lambda (res)
|
||||
(>= (balance res)
|
||||
(+ (balance acc) amt))))])])
|
||||
|
||||
(code:comment "section 3: the function definitions")
|
||||
(define balance account-balance)
|
||||
|
||||
(define (create amt) (account amt))
|
||||
|
||||
(define (withdraw a amt)
|
||||
(account (- (account-balance a) amt)))
|
||||
|
||||
(define (deposit a amt)
|
||||
(account (+ (account-balance a) amt)))
|
||||
]
|
||||
|
||||
The contracts in section 2 provide typical type-like guarantees for
|
||||
@racket[create] and @racket[balance]. For @racket[withdraw] and
|
||||
@racket[deposit], however, the contracts check and guarantee the more
|
||||
complicated constraints on @racket[balance] and @racket[deposit]. The
|
||||
contract on the second argument to @racket[withdraw] uses
|
||||
@racket[(balance acc)] to check whether the supplied withdrawal amount
|
||||
is small enough, where @racket[acc] is the name given within
|
||||
@racket[->d] to the function's first argument. The contract on the
|
||||
result of @racket[withdraw] uses both @racket[acc] and @racket[amt] to
|
||||
guarantee that no more than that requested amount was withdrawn. The
|
||||
contract on @racket[deposit] similarly uses @racket[acc] and
|
||||
@racket[amount] in the result contract to guarantee that at least as
|
||||
much money as provided was deposited into the account.
|
||||
|
||||
As written above, when a contract check fails, the error message is
|
||||
not great. The following revision uses @racket[flat-named-contract]
|
||||
within a helper function @racket[mk-account-contract] to provide
|
||||
better error messages.
|
||||
|
||||
@racketmod[
|
||||
racket
|
||||
|
||||
(code:comment "section 1: the contract definitions")
|
||||
(struct account (balance))
|
||||
(define amount/c natural-number/c)
|
||||
|
||||
(define msg> "account a with balance larger than ~a expected")
|
||||
(define msg< "account a with balance less than ~a expected")
|
||||
|
@ -333,70 +390,30 @@ racket
|
|||
|
||||
(code:comment "section 2: the exports")
|
||||
(provide/contract
|
||||
[create (amount . -> . account?)]
|
||||
[balance (account? . -> . amount)]
|
||||
[create (amount/c . -> . account?)]
|
||||
[balance (account? . -> . amount/c)]
|
||||
[withdraw (->d ([acc account?]
|
||||
[amt (and/c amount (<=/c (balance acc)))])
|
||||
[amt (and/c amount/c (<=/c (balance acc)))])
|
||||
()
|
||||
[result (mk-account-contract acc amt >= msg>)])]
|
||||
[deposit (->d ([acc account?]
|
||||
[amt amount])
|
||||
[amt amount/c])
|
||||
()
|
||||
[result (mk-account-contract acc amt <= msg<)])])
|
||||
|
||||
(code:comment "section 3: the function definitions")
|
||||
(define balance account-balance)
|
||||
|
||||
(define (create amt) (make-account amt))
|
||||
(define (create amt) (account amt))
|
||||
|
||||
(define (withdraw acc amt)
|
||||
(set-account-balance! acc (- (balance acc) amt))
|
||||
acc)
|
||||
(define (withdraw a amt)
|
||||
(account (- (account-balance a) amt)))
|
||||
|
||||
(define (deposit acc amt)
|
||||
(set-account-balance! acc (+ (balance acc) amt))
|
||||
acc)
|
||||
(define (deposit a amt)
|
||||
(account (+ (account-balance a) amt)))
|
||||
]
|
||||
|
||||
The second section is the export interface: @itemize[
|
||||
@item{@racket[create] consumes an initial deposit and
|
||||
produces an account. This kind of contract is just like a
|
||||
type in a statically typed language, except that statically
|
||||
typed languages usually don't support the type ``natural
|
||||
numbers'' (as a full-fledged subtype of numbers). }
|
||||
|
||||
@item{@racket[balance] consumes an account and computes its current balance.}
|
||||
|
||||
@item{@racket[withdraw] consumes an account, named @racket[acc], and an
|
||||
amount, @racket[amt]. In addition to being an @racket[amount], the
|
||||
latter must also be less than @racket[(balance acc)], i.e., the balance of
|
||||
the given account. That is, the contract for @racket[amt] depends on the
|
||||
value of @racket[acc], which is what the @racket[->d]
|
||||
contract combinator expresses.
|
||||
|
||||
The result contract is formed on the fly:
|
||||
@racket[(mk-account-contract acc amt > msg>)].
|
||||
It is an application of a contract-producing function that
|
||||
consumes an account, an amount, a comparison operator, and an error message (a
|
||||
format string). The result is a contract.
|
||||
}
|
||||
|
||||
@item{@racket[deposit]'s contract has been reformulated using the
|
||||
@racket[->d] combinator. }
|
||||
]
|
||||
|
||||
The code in the first section defines all those pieces that
|
||||
are needed for the formulation of the export contracts:
|
||||
@racket[account?], @racket[amount], error messages (format
|
||||
strings), and @racket[mk-account-contract]. The latter is a
|
||||
function that extracts the current balance from the given
|
||||
account and then returns a named contract, whose error
|
||||
message (contract name) is a string that refers to this
|
||||
balance. The resulting contract checks whether an account
|
||||
has a balance that is larger or smaller, depending on the
|
||||
given comparison operator, than the original balance.
|
||||
|
||||
@ctc-section[#:tag "arrow-d-eval-order"]{Ensuring that a Function Properly Modifies State}
|
||||
@ctc-section[#:tag "arrow-d-eval-order"]{Checking State Changes}
|
||||
|
||||
The @racket[->d] contract combinator can also ensure that a
|
||||
function only modifies state according to certain
|
||||
|
@ -451,70 +468,6 @@ contrast, if the contract for @racket[f] were
|
|||
(only changing the underscore to @racket[res]), then
|
||||
the result of @racket[get-x] would be @racket['(ctc f)].
|
||||
|
||||
@ctc-section[#:tag "case-lambda"]{Contracts for @racket[case-lambda]}
|
||||
|
||||
Dybvig, in Chapter 5 of the
|
||||
@link["http://www.racket.com/csug/"]{Chez Racket User's Guide},
|
||||
explains the meaning and pragmatics of
|
||||
@racket[case-lambda] with the following example (among
|
||||
others):
|
||||
|
||||
@racketblock[
|
||||
(define substring1
|
||||
(case-lambda
|
||||
[(s) (substring1 s 0 (string-length s))]
|
||||
[(s start) (substring1 s start (string-length s))]
|
||||
[(s start end) (substring s start end)]))
|
||||
]
|
||||
This version of @racket[substring] has one of the following signature:
|
||||
@itemize[
|
||||
@item{just a string, in which case it copies the string;}
|
||||
@item{a string and an index into the string, in which case it extracts the
|
||||
suffix of the string starting at the index; or }
|
||||
@item{a string a start index and an end index, in which case it extracts the
|
||||
fragment of the string between the two indices. }
|
||||
]
|
||||
|
||||
The contract for such a function is formed with the @racket[case->]
|
||||
combinator, which combines as many functional contracts as needed:
|
||||
@racketblock[
|
||||
(provide/contract
|
||||
[substring1
|
||||
(case->
|
||||
(string? . -> . string?)
|
||||
(string? natural-number/c . -> . string?)
|
||||
(string? natural-number/c natural-number/c . -> . string?))])
|
||||
]
|
||||
As you can see, the contract for @racket[substring1] combines three
|
||||
function contracts, just as many clauses as the explanation of its
|
||||
functionality required.
|
||||
|
||||
@;{
|
||||
This isn't supported anymore (yet...?). -robby
|
||||
|
||||
In the case of @racket[substring1], we also know that the indices
|
||||
that it consumes ought to be natural numbers less than the length of the
|
||||
given string. Since @racket[case->] just combines arrow contracts,
|
||||
adding such constraints is just a matter of strengthening the individual
|
||||
contracts:
|
||||
<racket>
|
||||
(provide/contract
|
||||
[substring1 (case->
|
||||
(string? . -> . string?)
|
||||
(->r ([s string?]
|
||||
[_ (and/c natural-number/c (</c (string-length s)))])
|
||||
string?)
|
||||
(->r ([s string?]
|
||||
[a (and/c natural-number/c (</c (string-length s)))]
|
||||
[o (and/c natural-number/c
|
||||
(>=/c a)
|
||||
(</c (string-length s)))])
|
||||
string?))])
|
||||
</racket>
|
||||
Here we used @racket[->r] to name the parameters and express the
|
||||
numeric constraints on them.
|
||||
}
|
||||
|
||||
@ctc-section[#:tag "multiple"]{Multiple Result Values}
|
||||
|
||||
The function @racket[split] consumes a list of @racket[char]s
|
||||
|
@ -534,8 +487,8 @@ The function @racket[split] consumes a list of @racket[char]s
|
|||
traversing a single list.
|
||||
|
||||
The contract for such a function can use the ordinary
|
||||
function arrow @racket[->], since it
|
||||
treats @racket[values] specially, when it appears as the
|
||||
function arrow @racket[->], since @racket[->]
|
||||
treats @racket[values] specially when it appears as the
|
||||
last result:
|
||||
@racketblock[
|
||||
(provide/contract
|
||||
|
@ -544,21 +497,21 @@ last result:
|
|||
]
|
||||
|
||||
The contract for such a function can also be written
|
||||
using @racket[->*], just like @racket[plus]:
|
||||
using @racket[->*]:
|
||||
@racketblock[
|
||||
(provide/contract
|
||||
[split (->* ((listof char?))
|
||||
()
|
||||
(values string? (listof char?)))])
|
||||
]
|
||||
As before the contract for the argument is wrapped in an
|
||||
As before, the contract for the argument with @racket[->*] is wrapped in an
|
||||
extra pair of parentheses (and must always be wrapped like
|
||||
that) and the empty pair of parentheses indicates that
|
||||
there are no optoinal arguments. The contracts for the
|
||||
there are no optional arguments. The contracts for the
|
||||
results are inside @racket[values]: a string and a list of
|
||||
characters.
|
||||
|
||||
Now suppose we also want to ensure that the first result of
|
||||
Now, suppose that we also want to ensure that the first result of
|
||||
@racket[split] is a prefix of the given word in list format. In that
|
||||
case, we need to use the @racket[->d] contract combinator:
|
||||
@racketblock[
|
||||
|
@ -584,7 +537,7 @@ Now suppose we also want to ensure that the first result of
|
|||
first contract strengthens the old one so that the result is a prefix of
|
||||
the given word.
|
||||
|
||||
This contract is expensive to check of course. Here is a slightly
|
||||
This contract is expensive to check, of course. Here is a slightly
|
||||
cheaper version:
|
||||
@racketblock[
|
||||
(provide/contract
|
||||
|
@ -593,9 +546,9 @@ This contract is expensive to check of course. Here is a slightly
|
|||
(values [s (string-len/c (length fl))]
|
||||
[c (listof char?)]))])
|
||||
]
|
||||
Click on @racket[string-len/c] to see what it does.
|
||||
|
||||
@ctc-section[#:tag "no-domain"]{Procedures of Some Fixed, but Statically Unknown Arity}
|
||||
|
||||
@ctc-section[#:tag "no-domain"]{Fixed but Statically Unknown Arities}
|
||||
|
||||
Imagine yourself writing a contract for a function that accepts some other
|
||||
function and a list of numbers that eventually applies the former to the
|
||||
|
|
|
@ -1,33 +1,33 @@
|
|||
#lang scribble/doc
|
||||
@(require scribble/manual
|
||||
scribble/eval
|
||||
scheme/sandbox
|
||||
racket/sandbox
|
||||
"guide-utils.ss"
|
||||
"contracts-utils.ss"
|
||||
(for-label scheme/contract))
|
||||
(for-label racket/contract))
|
||||
|
||||
@title[#:tag "contracts-gotchas"]{Gotchas}
|
||||
|
||||
@ctc-section{Contracts and @scheme[eq?]}
|
||||
@ctc-section{Contracts and @racket[eq?]}
|
||||
|
||||
As a general rule, adding a contract to a program should
|
||||
either leave the behavior of the program unchanged, or
|
||||
should signal a contract violation. And this is almost true
|
||||
for PLT Scheme contracts, with one exception: @scheme[eq?].
|
||||
for Racket contracts, with one exception: @racket[eq?].
|
||||
|
||||
The @scheme[eq?] procedure is designed to be fast and does
|
||||
The @racket[eq?] procedure is designed to be fast and does
|
||||
not provide much in the way of guarantees, except that if it
|
||||
returns true, it means that the two values behave
|
||||
identically in all respects. Internally, this is implemented
|
||||
as pointer equality at a low-level so it exposes information
|
||||
about how PLT Scheme is implemented (and how contracts are
|
||||
about how Racket is implemented (and how contracts are
|
||||
implemented).
|
||||
|
||||
Contracts interact poorly with @scheme[eq?] because function
|
||||
Contracts interact poorly with @racket[eq?] because function
|
||||
contract checking is implemented internally as wrapper
|
||||
functions. For example, consider this module:
|
||||
@schememod[
|
||||
scheme
|
||||
@racketmod[
|
||||
racket
|
||||
|
||||
(define (make-adder x)
|
||||
(if (= 1 x)
|
||||
|
@ -36,51 +36,51 @@ scheme
|
|||
(provide/contract [make-adder (-> number? (-> number? number?))])
|
||||
]
|
||||
|
||||
It exports the @scheme[make-adder] function that is the usual curried
|
||||
addition function, except that it returns Scheme's @scheme[add1] when
|
||||
its input is @scheme[1].
|
||||
It exports the @racket[make-adder] function that is the usual curried
|
||||
addition function, except that it returns Racket's @racket[add1] when
|
||||
its input is @racket[1].
|
||||
|
||||
You might expect that
|
||||
@schemeblock[
|
||||
@racketblock[
|
||||
(eq? (make-adder 1)
|
||||
(make-adder 1))
|
||||
]
|
||||
|
||||
would return @scheme[#t], but it does not. If the contract were
|
||||
changed to @scheme[any/c] (or even @scheme[(-> number? any/c)]), then
|
||||
the @scheme[eq?] call would return @scheme[#t].
|
||||
would return @racket[#t], but it does not. If the contract were
|
||||
changed to @racket[any/c] (or even @racket[(-> number? any/c)]), then
|
||||
the @racket[eq?] call would return @racket[#t].
|
||||
|
||||
Moral: do not use @scheme[eq?] on values that have contracts.
|
||||
Moral: Do not use @racket[eq?] on values that have contracts.
|
||||
|
||||
@ctc-section[#:tag "exists-gotcha"]{Exists Contracts and Predicates}
|
||||
|
||||
Much like the @scheme[eq?] example above, @scheme[#:∃] contracts
|
||||
Much like the @racket[eq?] example above, @racket[#:∃] contracts
|
||||
can change the behavior of a program.
|
||||
|
||||
Specifically,
|
||||
the @scheme[null?] predicate (and many other predicates) return @scheme[#f]
|
||||
for @scheme[#:∃] contracts, and changing one of those contracts to @scheme[any/c]
|
||||
means that @scheme[null?] might now return @scheme[#t] instead, resulting in
|
||||
the @racket[null?] predicate (and many other predicates) return @racket[#f]
|
||||
for @racket[#:∃] contracts, and changing one of those contracts to @racket[any/c]
|
||||
means that @racket[null?] might now return @racket[#t] instead, resulting in
|
||||
arbitrarily different behavior depending on this boolean might flow around
|
||||
in the program.
|
||||
|
||||
@defmodulelang[scheme/exists]
|
||||
@defmodulelang[racket/exists]
|
||||
|
||||
To work around the above problem, the
|
||||
@schememodname[scheme/exists] library behaves just like the @schememodname[scheme],
|
||||
but where predicates signal errors when given @scheme[#:∃] contracts.
|
||||
@racketmodname[racket/exists] library behaves just like the @racketmodname[racket],
|
||||
but where predicates signal errors when given @racket[#:∃] contracts.
|
||||
|
||||
Moral: do not use predicates on @scheme[#:∃] contracts, but if you're not sure, use
|
||||
@schememodname[scheme/exists] to be safe.
|
||||
Moral: Do not use predicates on @racket[#:∃] contracts, but if you're not sure, use
|
||||
@racketmodname[racket/exists] to be safe.
|
||||
|
||||
@ctc-section{Defining Recursive Contracts}
|
||||
|
||||
When defining a self-referential contract, it is natural to use
|
||||
@scheme[define]. For example, one might try to write a contract on
|
||||
@racket[define]. For example, one might try to write a contract on
|
||||
streams like this:
|
||||
|
||||
@(define e (make-base-eval))
|
||||
@(interaction-eval #:eval e (require scheme/contract))
|
||||
@(interaction-eval #:eval e (require racket/contract))
|
||||
@interaction[
|
||||
#:eval e
|
||||
(define stream/c
|
||||
|
@ -92,12 +92,12 @@ streams like this:
|
|||
@close-eval[e]
|
||||
|
||||
Unfortunately, this does not work because the value of
|
||||
@scheme[stream/c] is needed before it is defined. Put another way, all
|
||||
@racket[stream/c] is needed before it is defined. Put another way, all
|
||||
of the combinators evaluate their arguments eagerly, even thought the
|
||||
values that they accept do not.
|
||||
|
||||
Instead, use
|
||||
@schemeblock[
|
||||
@racketblock[
|
||||
(define stream/c
|
||||
(promise/c
|
||||
(or/c
|
||||
|
@ -106,27 +106,27 @@ Instead, use
|
|||
(recursive-contract stream/c)))))
|
||||
]
|
||||
|
||||
The use of @scheme[recursive-contract] delays the evaluation of the
|
||||
identifier @scheme[stream/c] until after the contract is first
|
||||
checked, long enough to ensure that @scheme[stream/c] is defined.
|
||||
The use of @racket[recursive-contract] delays the evaluation of the
|
||||
identifier @racket[stream/c] until after the contract is first
|
||||
checked, long enough to ensure that @racket[stream/c] is defined.
|
||||
|
||||
See also @ctc-link["lazy-contracts"].
|
||||
|
||||
@ctc-section{Using @scheme[set!] to Assign to Variables Provided via @scheme[provide/contract]}
|
||||
@ctc-section{Mixing @racket[set!] and @racket[provide/contract]}
|
||||
|
||||
The contract library assumes that variables exported via
|
||||
@scheme[provide/contract] are not assigned to, but does not enforce
|
||||
it. Accordingly, if you try to @scheme[set!] those variables, you
|
||||
@racket[provide/contract] are not assigned to, but does not enforce
|
||||
it. Accordingly, if you try to @racket[set!] those variables, you
|
||||
may be surprised. Consider the following example:
|
||||
|
||||
@interaction[
|
||||
(module server scheme
|
||||
(module server racket
|
||||
(define (inc-x!) (set! x (+ x 1)))
|
||||
(define x 0)
|
||||
(provide/contract [inc-x! (-> void?)]
|
||||
[x integer?]))
|
||||
|
||||
(module client scheme
|
||||
(module client racket
|
||||
(require 'server)
|
||||
|
||||
(define (print-latest) (printf "x is ~s\n" x))
|
||||
|
@ -138,15 +138,15 @@ may be surprised. Consider the following example:
|
|||
(require 'client)
|
||||
]
|
||||
|
||||
Both calls to @scheme[print-latest] print @scheme[0], even though the
|
||||
value of @scheme[x] has been incremented (and the change is visible
|
||||
inside the module @scheme[x]).
|
||||
Both calls to @racket[print-latest] print @racket[0], even though the
|
||||
value of @racket[x] has been incremented (and the change is visible
|
||||
inside the module @racket[x]).
|
||||
|
||||
To work around this, export accessor functions, rather than
|
||||
exporting the variable directly, like this:
|
||||
|
||||
@schememod[
|
||||
scheme
|
||||
@racketmod[
|
||||
racket
|
||||
|
||||
(define (get-x) x)
|
||||
(define (inc-x!) (set! x (+ x 1)))
|
||||
|
@ -155,5 +155,5 @@ scheme
|
|||
[get-x (-> integer?)])
|
||||
]
|
||||
|
||||
Moral: This is a bug we hope to address in a future release.
|
||||
Moral: This is a bug that we will address in a future release.
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
scribble/eval
|
||||
"guide-utils.ss"
|
||||
"contracts-utils.ss"
|
||||
(for-label scheme/contract))
|
||||
(for-label racket/contract))
|
||||
|
||||
@title[#:tag "contract-boundaries"]{Contracts and Boundaries}
|
||||
|
||||
|
@ -16,72 +16,73 @@ A contract thus establishes a boundary between the two parties. Whenever a
|
|||
value crosses this boundary, the contract monitoring system performs contract
|
||||
checks, making sure the partners abide by the established contract.
|
||||
|
||||
In this spirit, PLT Scheme supports contracts only at module
|
||||
In this spirit, Racket encourages contracts mainly at module
|
||||
boundaries. Specifically, programmers may attach contracts to
|
||||
@scheme[provide] clauses and thus impose constraints and promises on the use
|
||||
@racket[provide] clauses and thus impose constraints and promises on the use
|
||||
of exported values. For example, the export specification
|
||||
@schememod[
|
||||
scheme
|
||||
@racketmod[
|
||||
racket
|
||||
|
||||
(provide/contract
|
||||
[amount positive?])
|
||||
(define amount ...)
|
||||
]
|
||||
|
||||
promises to all clients of the above module that amount will
|
||||
promises to all clients of the above module that the value of @racket[amount] will
|
||||
always be a positive number. The contract system monitors
|
||||
the module's obligation carefully. Every time a client
|
||||
refers to @scheme[amount], the monitor checks that the value
|
||||
of @scheme[amount] is indeed a positive number.
|
||||
refers to @racket[amount], the monitor checks that the value
|
||||
of @racket[amount] is indeed a positive number.
|
||||
|
||||
The contracts library is built into the Scheme language, but
|
||||
if you wish to use @scheme[scheme/base], you can explicitly
|
||||
The contracts library is built into the Racket language, but
|
||||
if you wish to use @racket[racket/base], you can explicitly
|
||||
require the contracts library like this:
|
||||
|
||||
@schememod[
|
||||
scheme/base
|
||||
(require scheme/contract) (code:comment "now we can write contracts")
|
||||
@racketmod[
|
||||
racket/base
|
||||
(require racket/contract) (code:comment "now we can write contracts")
|
||||
|
||||
(provide/contract
|
||||
[amount positive?])
|
||||
(define amount ...)
|
||||
]
|
||||
|
||||
@ctc-section[#:tag "amount0"]{A First Contract Violation}
|
||||
@ctc-section[#:tag "amount0"]{Contract Violations}
|
||||
|
||||
Suppose the creator of the module had written
|
||||
@schememod[
|
||||
scheme
|
||||
If we bind @scheme[amount] to a number that is not positive,
|
||||
|
||||
@racketmod[
|
||||
racket
|
||||
|
||||
(provide/contract
|
||||
[amount positive?])
|
||||
|
||||
(define amount 0)]
|
||||
|
||||
When this module is required, the monitoring
|
||||
then, when the module is required, the monitoring
|
||||
system signals a violation of the contract and
|
||||
blames the module for breaking its promises.
|
||||
|
||||
@ctc-section[#:tag "qamount"]{A Subtle Contract Violation}
|
||||
@; @ctc-section[#:tag "qamount"]{A Subtle Contract Violation}
|
||||
|
||||
Suppose we write this module
|
||||
@schememod[
|
||||
scheme
|
||||
An even bigger mistake would be to bind @racket[amount]
|
||||
to a non-number value:
|
||||
|
||||
@racketmod[
|
||||
racket
|
||||
|
||||
(provide/contract
|
||||
[amount positive?])
|
||||
|
||||
(define amount 'amount)
|
||||
]
|
||||
|
||||
In that case, the monitoring system applies
|
||||
@scheme[positive?] to a symbol, but @scheme[positive?]
|
||||
In this case, the monitoring system will apply
|
||||
@racket[positive?] to a symbol, but @racket[positive?]
|
||||
reports an error, because its domain is only numbers. To
|
||||
make the contract capture our intentions for all Scheme
|
||||
make the contract capture our intentions for all Racket
|
||||
values, we can ensure that the value is both a number and is
|
||||
positive, combining the two contracts with @scheme[and/c]:
|
||||
positive, combining the two contracts with @racket[and/c]:
|
||||
|
||||
@schemeblock[
|
||||
@racketblock[
|
||||
(provide/contract
|
||||
[amount (and/c number? positive?)])
|
||||
]
|
||||
|
@ -95,13 +96,13 @@ provide/contract'd. This is currently buggy so this
|
|||
discussion is elided. Here's the expansion of
|
||||
the requiring module, just to give an idea:
|
||||
|
||||
(module m mzscheme
|
||||
(module m racket
|
||||
(require mzlib/contract)
|
||||
(provide/contract [x x-ctc]))
|
||||
|
||||
(module n mzscheme (require m) (define (f) ... x ...))
|
||||
(module n racket (require m) (define (f) ... x ...))
|
||||
==>
|
||||
(module n mzscheme
|
||||
(module n racket
|
||||
(require (rename m x x-real))
|
||||
(define x (apply-contract x-real x-ctc ...))
|
||||
(define (f) ... x ...))
|
||||
|
@ -122,9 +123,9 @@ Of course, this breaks assignment to the provided variable.
|
|||
|
||||
<table src="simple.ss">
|
||||
<tr><td bgcolor="e0e0fa">
|
||||
<scheme>
|
||||
<racket>
|
||||
;; Language: Pretty Big
|
||||
(module a mzscheme
|
||||
(module a racket
|
||||
(require mzlib/contract)
|
||||
|
||||
(provide/contract
|
||||
|
@ -139,7 +140,7 @@ Of course, this breaks assignment to the provided variable.
|
|||
|
||||
(define (do-it) <font color="red">(set! amount -4)</font>))
|
||||
|
||||
(module b mzscheme
|
||||
(module b racket
|
||||
(require a)
|
||||
|
||||
(printf "~s~n" amount)
|
||||
|
@ -147,17 +148,17 @@ Of course, this breaks assignment to the provided variable.
|
|||
(printf "~s~n" amount))
|
||||
|
||||
(require b)
|
||||
</scheme>
|
||||
</racket>
|
||||
<td bgcolor="beige" valign="top">
|
||||
<pre>
|
||||
|
||||
the "server" module
|
||||
this allows us to write contracts
|
||||
|
||||
export @scheme[amount] with a contract
|
||||
export @racket[amount] with a contract
|
||||
|
||||
|
||||
export @scheme[do-it] without contract
|
||||
export @racket[do-it] without contract
|
||||
|
||||
|
||||
|
||||
|
@ -168,65 +169,71 @@ set amount to 4,
|
|||
the "client" module
|
||||
requires functionality from a
|
||||
|
||||
first reference to @scheme[amount] (okay)
|
||||
a call to @scheme[do-it],
|
||||
second reference to @scheme[amount] (fail)
|
||||
first reference to @racket[amount] (okay)
|
||||
a call to @racket[do-it],
|
||||
second reference to @racket[amount] (fail)
|
||||
|
||||
</pre> </table>
|
||||
|
||||
<p><strong>Note:</strong> The above example is mostly self-explanatory. Take a
|
||||
look at the lines in red, however. Even though the call to @scheme[do-it]
|
||||
sets @scheme[amount] to -4, this action is <strong>not</strong> a contract
|
||||
look at the lines in red, however. Even though the call to @racket[do-it]
|
||||
sets @racket[amount] to -4, this action is <strong>not</strong> a contract
|
||||
violation. The contract violation takes place only when the client module
|
||||
(@scheme[b]) refers to @scheme[amount] again and the value flows across
|
||||
(@racket[b]) refers to @racket[amount] again and the value flows across
|
||||
the module boundary for a second time.
|
||||
|
||||
</question>
|
||||
}
|
||||
|
||||
@;{
|
||||
|
||||
@ctc-section[#:tag "obligations"]{Imposing Obligations on a Module's Clients}
|
||||
|
||||
On occasion, a module may want to enter a contract with
|
||||
another module only if the other module abides by certain
|
||||
rules. In other words, the module isn't just promising some
|
||||
services, it also demands the client to deliver
|
||||
something. This kind of thing happens when a module exports
|
||||
a function, an object, a class or other values that enable
|
||||
something. That situation may happen when a module exports
|
||||
a function, an object, a class, or some other construct that enables
|
||||
values to flow in both directions.
|
||||
|
||||
@ctc-section{Experimenting with Examples}
|
||||
}
|
||||
|
||||
@ctc-section{Experimenting with Contracts and Modules}
|
||||
|
||||
All of the contracts and module in this chapter (excluding those just
|
||||
following) are written using the standard @tt{#lang} syntax for
|
||||
describing modules. Thus, if you extract examples from this chapter in
|
||||
order to experiment with the behavior of the contract system, you
|
||||
would have to make multiple files.
|
||||
describing modules. Since modules serve as the boundary between
|
||||
parties in a contract, examples involve multiple modules.
|
||||
|
||||
To rectify this, PLT Scheme provides a special language, called
|
||||
@schememodname[scheme/load]. The contents of such a module is other modules (and
|
||||
@scheme[require] statements), using the parenthesized syntax for a
|
||||
module. For example, to try the example earlier in this section, you
|
||||
would write:
|
||||
@schememod[
|
||||
scheme/load
|
||||
To experiment with multiple modules within a single module or within
|
||||
DrRacket's @tech{definitions area}, use the
|
||||
@racketmodname[racket/load] language. The contents of such a module
|
||||
can be other modules (and @racket[require] statements), using the
|
||||
longhand parenthesized syntax for a module (see
|
||||
@secref["module-syntax"]). For example, try the example earlier in
|
||||
this section as follows:
|
||||
|
||||
(module m scheme
|
||||
(define amount 150)
|
||||
(provide/contract [amount (and/c number? positive?)]))
|
||||
@racketmod[
|
||||
racket/load
|
||||
|
||||
(module n scheme
|
||||
(module m racket
|
||||
(provide/contract [amount (and/c number? positive?)])
|
||||
(define amount 150))
|
||||
|
||||
(module n racket
|
||||
(require 'm)
|
||||
(+ amount 10))
|
||||
|
||||
(require 'n)]
|
||||
|
||||
Each of the modules and their contracts are wrapped in parentheses
|
||||
with the @scheme[module] keyword at the front. The first argument to
|
||||
@scheme[module] should be the name of the module, so it can be used in
|
||||
a subsequent @scheme[require] statement (note that in the
|
||||
@scheme[require], the name of the module must be prefixed with a
|
||||
quote). The second argument to @scheme[module] is the language (what
|
||||
would have come after @tt{#lang} in the usual notation), and the
|
||||
remaining arguments are the body of the module. After all of the
|
||||
modules, there must a @scheme[require] to kick things off.
|
||||
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 a quote). The second form
|
||||
after @racket[module] is the language, and the remaining forms are the
|
||||
body of the module. After all of the modules, a @racket[require]
|
||||
starts one of the modules plus anything that is @racket[require]s.
|
||||
|
||||
|
||||
|
|
|
@ -3,136 +3,161 @@
|
|||
scribble/eval
|
||||
"guide-utils.ss"
|
||||
"contracts-utils.ss"
|
||||
(for-label scheme/contract))
|
||||
(for-label racket/contract))
|
||||
|
||||
@title[#:tag "contract-func"]{Simple Contracts on Functions}
|
||||
|
||||
When a module exports a function, it establishes two
|
||||
channels of communication between itself and the client
|
||||
module that imports the function. If the client module calls
|
||||
the function, it sends a value into the ``server''
|
||||
module. Conversely, if such a function call ends and the
|
||||
function returns a value, the ``server'' module sends a
|
||||
value back to the ``client'' module.
|
||||
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
|
||||
function with its domain and range is
|
||||
|
||||
It is important to keep this picture in mind when you read the explanations
|
||||
of the various ways of imposing contracts on functions.
|
||||
|
||||
@ctc-section[#:tag "argcontract"]{Restricting the Arguments of a Function}
|
||||
|
||||
Functions usually don't work on all possible Scheme values but only on a
|
||||
select subset such as numbers, booleans, etc. Here is a module that may
|
||||
represent a bank account:
|
||||
|
||||
@schememod[
|
||||
scheme
|
||||
|
||||
(provide/contract
|
||||
[create (-> string? number? any)]
|
||||
[deposit (-> number? any)])
|
||||
|
||||
(define amount 0)
|
||||
(define (create name initial-deposit) ...)
|
||||
(define (deposit a) (set! amount (+ amount a)))
|
||||
]
|
||||
|
||||
It exports two functions:
|
||||
@itemize[
|
||||
|
||||
@item{@scheme[create]: The function's contract says that it consumes two
|
||||
arguments, a string and a number, and it promises nothing about the return value. }
|
||||
|
||||
@item{@scheme[deposit]: The function's contract demands from the client modules
|
||||
that they apply it to numbers. It promises nothing about the return value. }]
|
||||
|
||||
If a ``client'' module were to apply @scheme[deposit] to
|
||||
@scheme['silly], it would violate the contract. The
|
||||
contract monitoring system would catch this violation and
|
||||
blame ``client'' for breaking the contract with the above
|
||||
module.
|
||||
|
||||
@bold{Note:} Instead of @scheme[any] you could also use the
|
||||
more specific contract @scheme[void?], which says that the function will
|
||||
always return the @scheme[(void)] value. This contract, however, would require
|
||||
the contract monitoring system to check the return value every time the function
|
||||
is called, even though the ``client'' module can't do much with this value
|
||||
anyway. In contrast, @scheme[any] tells the monitoring system @italic{not}
|
||||
to check the return value. Additionally, it tells a potential client that the
|
||||
``server'' module @italic{makes no promises at all} about the function's return
|
||||
value.
|
||||
|
||||
@ctc-section[#:tag "arrow"]{Arrows}
|
||||
|
||||
It is natural to use an arrow to say that an exported value is a
|
||||
function. In decent high schools, you learn that a function has a domain
|
||||
and a range, and that you write this fact down like this:
|
||||
@schemeblock[
|
||||
@racketblock[
|
||||
f : A -> B
|
||||
]
|
||||
Here the @scheme[A] and @scheme[B] are sets; @scheme[A] is the
|
||||
domain and @scheme[B] is the range.
|
||||
|
||||
Functions in a programming language have domains and ranges, too. In
|
||||
statically typed languages, you write down the names of types for each
|
||||
argument and for the result. When all you have, however, is a Scheme name,
|
||||
such as @scheme[create] or @scheme[deposit], you want to tell the
|
||||
reader what the name represents (a function) and, if it is a function (or
|
||||
some other complex value) what the pieces are supposed to be. This is why
|
||||
we use a @scheme[->] to say ``hey, expect this to be a function.''
|
||||
where @racket[A] is the domain of the function and @racket[B] is the
|
||||
range.
|
||||
|
||||
Functions in a programming language have domains and ranges, too, and
|
||||
a contract can ensure that a function receives only values in its
|
||||
range and produces only values in its domain. A @racket[->] creates
|
||||
such a contract for a function. The forms after a @racket[->] specify
|
||||
contracts for the domains and finally a contract for the range.
|
||||
|
||||
Here is a module that might represent a bank account:
|
||||
|
||||
@racketmod[
|
||||
racket
|
||||
|
||||
So @scheme[->] says ``this is a contract for a function.'' What follows
|
||||
in a function contracts are contracts (sub-contracts if you wish) that tell
|
||||
the reader what kind of arguments to expect and what kind of a result the
|
||||
function produces. For example,
|
||||
@schemeblock[
|
||||
(provide/contract
|
||||
[create (-> string? number? boolean? account?)])
|
||||
[deposit (-> number? any)]
|
||||
[balance (-> number?)])
|
||||
|
||||
(define amount 0)
|
||||
(define (deposit a) (set! amount (+ amount a)))
|
||||
(define (balance) amount)
|
||||
]
|
||||
says that @scheme[create] is a function of three arguments: a string, a
|
||||
number, and a boolean. Its result is an account.
|
||||
|
||||
In short, the arrow @scheme[->] is a @italic{contract
|
||||
combinator}. Its purpose is to combine other contracts into a contract
|
||||
that says ``this is a function @italic{and} its arguments and its result
|
||||
are like that.''
|
||||
The module exports two functions:
|
||||
|
||||
@ctc-section[#:tag "dots"]{Infix Contract Notation}
|
||||
@itemize[
|
||||
|
||||
If you are used to mathematics, you like the arrow in 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 people's code:
|
||||
@item{@racket[deposit], which accepts a number and returns some value
|
||||
that is not specified in the contract, and}
|
||||
|
||||
@schemeblock[
|
||||
@item{@racket[balance], which returns a number indicating the current
|
||||
balance of the account.}
|
||||
|
||||
]
|
||||
|
||||
When a module exports a function, it establishes two channels of
|
||||
communication between itself as a ``server'' and the ``client'' module
|
||||
that imports the function. If the client module calls the function, it
|
||||
sends a value into the server module. Conversely, if such a function
|
||||
call ends and the function returns a value, the server module sends a
|
||||
value back to the client module. This client--server distinction is
|
||||
important, because when something goes wrong, one or the other of the
|
||||
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
|
||||
the above module. In contrast, if the @racket[balance] function were
|
||||
to return @racket['broke], the contract-monitoring system
|
||||
would blame the server module.
|
||||
|
||||
A @racket[->] by itself is not a contract; it is a @deftech{contract
|
||||
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
|
||||
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
|
||||
people's code:
|
||||
|
||||
@racketblock[
|
||||
(provide/contract
|
||||
[create (string? number? boolean? . -> . account?)])
|
||||
[deposit (number? . -> . any)])
|
||||
]
|
||||
|
||||
If a PLT Scheme S-expression contains two dots with a symbol in the middle,
|
||||
the reader re-arranges the S-expression and place the symbol at the front. Thus,
|
||||
@schemeblock[
|
||||
(string? number? boolean? . -> . account?)
|
||||
If a Racket S-expression contains two dots with a symbol in the middle,
|
||||
the reader re-arranges the S-expression and place the symbol at the front,
|
||||
as described in @secref["lists-and-syntax"]. Thus,
|
||||
|
||||
@racketblock[
|
||||
(number? . -> . any)
|
||||
]
|
||||
is really just a short-hand for
|
||||
@schemeblock[
|
||||
(-> string? number? boolean? account?)
|
||||
|
||||
is just another way of writing
|
||||
|
||||
@racketblock[
|
||||
(-> number? any)
|
||||
]
|
||||
Of course, placing the arrow to the left of the range follows not only
|
||||
mathematical tradition but also that of typed functional languages.
|
||||
|
||||
@ctc-section[#:tag "own"]{Rolling Your Own Contracts for Function Arguments}
|
||||
@; ----------------------------------------------------------------------
|
||||
@section{@racket[any] and @racket[any/c]}
|
||||
|
||||
The @scheme[deposit] function adds the given number to the value of
|
||||
@scheme[amount]. While the function's contract prevents clients from
|
||||
applying it to non-numbers, the contract still allows them to apply the function
|
||||
to complex numbers, negative numbers, or inexact numbers, all of which do not
|
||||
represent amounts of money.
|
||||
The @racket[any] contract used for @racket[deposit] matches any kind
|
||||
of result, and it can only be used in the range position of a function
|
||||
contract. Instead of @racket[any] above, we could use the more
|
||||
specific contract @racket[void?], which says that the function will
|
||||
always return the @racket[(void)] value. The @racket[void?] contract,
|
||||
however, would require the contract monitoring system to check the
|
||||
return value every time the function is called, even though the
|
||||
``client'' module can't do much with the value. In contrast,
|
||||
@racket[any] tells the monitoring system @italic{not} to check the
|
||||
return value, it tells a potential client that the ``server'' module
|
||||
@italic{makes no promises at all} about the function's return value,
|
||||
even whether it is a single value or multiple values.
|
||||
|
||||
To this end, the contract system allows programmers to define their own
|
||||
contracts:
|
||||
The @racket[any/c] contract is similar to @racket[any], in that it
|
||||
makes no demands on a value. Unlike @scheme[any], @racket[any/c]
|
||||
indicates a single value, and it is suitable for use as an argument
|
||||
contract. Using @racket[any/c] as a range contract imposes a check
|
||||
that the function produces a single value. That is,
|
||||
|
||||
@schememod[
|
||||
scheme
|
||||
@racketblock[(-> integer? any)]
|
||||
|
||||
describes a function that accepts and integer and returns any number of
|
||||
values, while
|
||||
|
||||
@racketblock[(-> integer? any/c)]
|
||||
|
||||
describes a function that accepts an integer and produces a single
|
||||
result (but does not say anything more about the result). The function
|
||||
|
||||
@racketblock[
|
||||
(define (f x) (values (+ x 1) (- x 1)))
|
||||
]
|
||||
|
||||
matches @racket[(-> integer? any)], but not @racket[(-> integer? any/c)].
|
||||
|
||||
Use @racket[any/c] as a result contract when it is particularly
|
||||
important to promise a single result from a function. Use @racket[any]
|
||||
when you want to promise as little as possible (and incur as little
|
||||
checking as possible) for a function's result.
|
||||
|
||||
@; ------------------------------------------------------------------------
|
||||
|
||||
@ctc-section[#:tag "own"]{Rolling Your Own Contracts}
|
||||
|
||||
The @racket[deposit] function adds the given number to the value of
|
||||
@racket[amount]. While the function's contract prevents clients from
|
||||
applying it to non-numbers, the contract still allows them to apply
|
||||
the function to complex numbers, negative numbers, or inexact numbers,
|
||||
none of which sensibly represent amounts of money.
|
||||
|
||||
The contract system allows programmers to define their own contracts
|
||||
as functions:
|
||||
|
||||
@racketmod[
|
||||
racket
|
||||
|
||||
(define (amount? a)
|
||||
(and (number? a) (integer? a) (exact? a) (>= a 0)))
|
||||
|
@ -141,80 +166,66 @@ scheme
|
|||
(code:comment "an amount is a natural number of cents")
|
||||
(code:comment "is the given number an amount?")
|
||||
[deposit (-> amount? any)]
|
||||
[amount? (-> any/c boolean?)])
|
||||
[amount? (-> any/c boolean?)]
|
||||
[balance (-> amount?)])
|
||||
|
||||
(define this 0)
|
||||
(define (deposit a) (set! this (+ this a)))
|
||||
(define amount 0)
|
||||
(define (deposit a) (set! amount (+ amount a)))
|
||||
(define (balance) amount)
|
||||
]
|
||||
|
||||
The module introduces a
|
||||
predicate, @scheme[amount?]. The @scheme[provide]
|
||||
clause refers to this predicate, as a contract, for its
|
||||
specification of the contract of
|
||||
@scheme[deposit].
|
||||
This module define an @racket[amount?] function as uses it as a
|
||||
contract within @racket[->] contracts. When a client calls the
|
||||
@racket[deposit] function as exported with the contract @racket[(->
|
||||
amount? any)], it must supply an exact, nonnegative integer, otherwise
|
||||
the @racket[amount?] function applied to the argument will return
|
||||
@racket[#f], which will cause the contract-monitoring system to blame
|
||||
the client. Similarly, the server module must provide an exact,
|
||||
nonnegative integer as the result of @racket[balance] to remain
|
||||
blameless.
|
||||
|
||||
Of course it makes no sense to restrict a channel of
|
||||
communication to values that the client doesn't
|
||||
understand. Therefore the module also exports
|
||||
the @scheme[amount?] predicate itself, with a contract
|
||||
saying that it accepts an arbitrary value and returns a
|
||||
boolean.
|
||||
Of course, it makes no sense to restrict a channel of communication to
|
||||
values that the client doesn't understand. Therefore the module also
|
||||
exports the @racket[amount?] predicate itself, with a contract saying
|
||||
that it accepts an arbitrary value and returns a boolean.
|
||||
|
||||
In this case, we could also have used @scheme[natural-number/c], which
|
||||
is a contract defined in @schememodname[scheme/contract] that is
|
||||
equivalent to @scheme[amount] (modulo the name):
|
||||
|
||||
@schememod[
|
||||
scheme
|
||||
In this case, we could also have used @racket[natural-number/c] in
|
||||
place of @racket[amount?], since it implies exactly the same check:
|
||||
|
||||
@racketblock[
|
||||
(provide/contract
|
||||
(code:comment "an amount is a natural number of cents")
|
||||
[deposit (-> natural-number/c any)])
|
||||
|
||||
(define this 0)
|
||||
(define (deposit a) (set! this (+ this a)))
|
||||
[deposit (-> natural-number/c any)]
|
||||
[balance (-> natural-number/c)])
|
||||
]
|
||||
|
||||
Lesson: learn about the built-in contracts in @schememodname[scheme/contract].
|
||||
Every function that accepts one argument can be treated as a predicate
|
||||
and thus used as a contract. For combining existing checks into a new
|
||||
one, however, contract combinators such as @racket[and/c] and
|
||||
@racket[or/c] are often useful. For example, here is yet another way
|
||||
to write the contracts above:
|
||||
|
||||
@ctc-section[#:tag "and-or"]{The @scheme[and/c], @scheme[or/c], and @scheme[listof] Contract Combinators}
|
||||
|
||||
Both @scheme[and/c] and @scheme[or/c] combine contracts and
|
||||
they do what you expect them to do.
|
||||
|
||||
For example, if we didn't have @scheme[natural-number/c], the
|
||||
@scheme[amount?] contract is a bit opaque. Instead, we would define it
|
||||
as follows:
|
||||
|
||||
@schememod[
|
||||
scheme
|
||||
|
||||
(define amount
|
||||
@racketblock[
|
||||
(define amount/c
|
||||
(and/c number? integer? exact? (or/c positive? zero?)))
|
||||
|
||||
(provide/contract
|
||||
(code:comment "an amount is a natural number of cents")
|
||||
(code:comment "is the given number an amount?")
|
||||
[deposit (-> amount any)])
|
||||
|
||||
(define this 0)
|
||||
(define (deposit a) (set! this (+ this a)))
|
||||
[deposit (-> amount/c any)]
|
||||
[balance (-> amount/c)])
|
||||
]
|
||||
|
||||
That is, amount is a contract that enforces the following conditions: the
|
||||
value satisfies @scheme[number?] and @scheme[integer?] and
|
||||
@scheme[exact?] and is either @scheme[positive?] or
|
||||
@scheme[zero?].
|
||||
Other values also serve double duty as contracts. For example, if a
|
||||
function accepts a number or @racket[#f], @racket[(or/c number? #f)]
|
||||
suffices. Similarly, the @racket[amount/c] contract could have been
|
||||
written with a @racket[0] in place of @racket[zero?]. If you use a
|
||||
regular expression as a contract, the contract accepts strings and
|
||||
byte strings that match the regular expression.
|
||||
|
||||
Oh, we almost forgot. What do you think @scheme[(listof char?)]
|
||||
means? Hint: it is a contract!
|
||||
Naturally, you can mix your own contract-implementing functions with
|
||||
combinators like @racket[and/c]. Here is a module for creating strings
|
||||
from banking records:
|
||||
|
||||
@ctc-section[#:tag "range"]{Restricting the Range of a Function}
|
||||
|
||||
Consider a utility module for creating strings from banking records:
|
||||
|
||||
@schememod[
|
||||
scheme
|
||||
@racketmod[
|
||||
racket
|
||||
|
||||
(define (has-decimal? str)
|
||||
(define L (string-length str))
|
||||
|
@ -230,22 +241,19 @@ scheme
|
|||
[format-nat (-> natural-number/c
|
||||
(and/c string? has-decimal?))])
|
||||
]
|
||||
The contract of the exported function @scheme[format-number] specifies that
|
||||
the function consumes a number and produces a string.
|
||||
|
||||
The contract of the exported function @scheme[format-nat] is more
|
||||
interesting than the one of @scheme[format-number]. It consumes only
|
||||
The contract of the exported function @racket[format-number] specifies
|
||||
that the function consumes a number and produces a string. The
|
||||
contract of the exported function @racket[format-nat] is more
|
||||
interesting than the one of @racket[format-number]. It consumes only
|
||||
natural numbers. Its range contract promises a string that has a
|
||||
@litchar{.} in the third position from the right.
|
||||
|
||||
@(exercise) Strengthen the promise of the range contract for
|
||||
@scheme[format-nat] so that it admits only strings with digits and a single
|
||||
dot.
|
||||
If we want to strengthen the promise of the range contract for
|
||||
@racket[format-nat] so that it admits only strings with digits and a single
|
||||
dot, we could write it like this:
|
||||
|
||||
@(solution)
|
||||
|
||||
@schememod[
|
||||
scheme
|
||||
@racketmod[
|
||||
racket
|
||||
|
||||
(define (digit-char? x)
|
||||
(member x '(#\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9 #\0)))
|
||||
|
@ -263,11 +271,10 @@ scheme
|
|||
(andmap digit-char?
|
||||
(string->list (substring str (- L 2) L)))))
|
||||
|
||||
(provide/contract
|
||||
...
|
||||
(code:comment "convert a number to a string")
|
||||
[format-number (-> number? string?)]
|
||||
....
|
||||
|
||||
(provide/contract
|
||||
....
|
||||
(code:comment "convert an amount (natural number) of cents")
|
||||
(code:comment "into a dollar based string")
|
||||
[format-nat (-> natural-number/c
|
||||
|
@ -275,23 +282,22 @@ scheme
|
|||
is-decimal-string?))])
|
||||
]
|
||||
|
||||
Alternately, in this case, we could use a regular expression as a
|
||||
contract:
|
||||
|
||||
@ctc-section[#:tag "coercion"]{Contracts Coerced from Other Values}
|
||||
@racketmod[
|
||||
racket
|
||||
|
||||
The contract library treats a number of Scheme values as if they are
|
||||
contracts directly. We've already seen one main use of that: predicates. Every
|
||||
function that accepts one argument can be treated as a predicate
|
||||
and thus used as a contract.
|
||||
(provide/contract
|
||||
....
|
||||
(code:comment "convert an amount (natural number) of cents")
|
||||
(code:comment "into a dollar based string")
|
||||
[format-nat (-> natural-number/c
|
||||
(and/c string?
|
||||
#rx"[0-9]*\\.[0-9][0-9][0-9]"))])
|
||||
]
|
||||
|
||||
But many other values also play double duty as contracts.
|
||||
For example, if your function accepts a number or @scheme[#f],
|
||||
@scheme[(or/c number? #f)] suffices. Similarly, the @scheme[result/c] contract
|
||||
could have been written with a @scheme[0] in place of @scheme[zero?].
|
||||
|
||||
Even better, if you use a regular expression as a contract, the contract
|
||||
accepts strings that match the regular expression. For example,
|
||||
the @scheme[is-decimal-string?] predicate could have been written
|
||||
@scheme[#rx"[0-9]*\\.[0-9][0-9][0-9]"].
|
||||
@; ------------------------------------------------------------------------
|
||||
|
||||
@ctc-section{Contracts on Higher-order Functions}
|
||||
|
||||
|
@ -302,35 +308,84 @@ themselves, can be used as contracts on the arguments and
|
|||
results of a function.
|
||||
|
||||
For example,
|
||||
@schemeblock[(-> integer? (-> integer? integer?))]
|
||||
is a contract that describes a curried function. It matches
|
||||
functions that accept one argument and then return another
|
||||
function accepting a second argument before finally
|
||||
returning an integer.
|
||||
|
||||
This contract
|
||||
@schemeblock[(-> (-> integer? integer?) integer?)]
|
||||
describes functions that accept other functions as inputs.
|
||||
@racketblock[(-> integer? (-> integer? integer?))]
|
||||
|
||||
@ctc-section{The Difference Between @scheme[any] and @scheme[any/c]}
|
||||
is a contract that describes a curried function. It matches functions
|
||||
that accept one argument and then return another function accepting a
|
||||
second argument before finally returning an integer. If a server
|
||||
exports a function @racket[make-adder] with this contract, and if
|
||||
@racket[make-adder] returns a value other than a function, then the
|
||||
server is to blame. If @racket[make-adder] does return a function, but
|
||||
the resulting function is applied to a value other than an integer,
|
||||
then the client is to blame.
|
||||
|
||||
The contract @scheme[any/c] accepts any value, and
|
||||
@scheme[any] is a keyword that can appear in the range of
|
||||
the function contracts (@scheme[->], @scheme[->*], and
|
||||
@scheme[->d]), so it is natural to wonder what the
|
||||
difference between these two contracts is:
|
||||
@schemeblock[
|
||||
(-> integer? any)
|
||||
(-> integer? any/c)
|
||||
Similarly, the contract
|
||||
|
||||
@racketblock[(-> (-> integer? integer?) integer?)]
|
||||
|
||||
describes functions that accept other functions as its input. If a
|
||||
server exports a function @racket[twice] with this contract and the
|
||||
@racket[twice] is applied to a value other than a function of one
|
||||
argument, then the client is to blame. If @racket[twice] is applied to
|
||||
a function of one argument and @racket[twice] calls the give function
|
||||
on a value other than an integer, then the server is to blame.
|
||||
|
||||
@; ----------------------------------------------------------------------
|
||||
|
||||
@ctc-section[#:tag "flat-named-contracts"]{Contract Messages with ``???''}
|
||||
|
||||
You wrote your module. You added contracts. You put them into the interface
|
||||
so that client programmers have all the information from interfaces. It's a
|
||||
piece of art:
|
||||
@racketmod[
|
||||
racket
|
||||
|
||||
(provide/contract
|
||||
[deposit (-> (lambda (x)
|
||||
(and (number? x) (integer? x) (>= x 0)))
|
||||
any)])
|
||||
|
||||
(define this 0)
|
||||
(define (deposit a) ...)
|
||||
]
|
||||
|
||||
Both allow any result, right? There is one important difference:
|
||||
in the first case, the function may return anything at
|
||||
all, including multiple values. In the second case, the
|
||||
function may return any value, but not more than one. For
|
||||
example, this function:
|
||||
@schemeblock[
|
||||
(define (f x) (values (+ x 1) (- x 1)))
|
||||
]
|
||||
meets the first contract, but not the second one.}
|
||||
Several clients used your module. Others used their
|
||||
modules in turn. And all of a sudden one of them sees this error
|
||||
message:
|
||||
|
||||
@inset-flow{@racketerror{bank-client broke the contract (-> ??? any)
|
||||
it had with myaccount on deposit; expected <???>, given: -10}}
|
||||
|
||||
Clearly, @racket[bank-client] is a module that uses @racket[myaccount]
|
||||
but what is the @racketerror{???} doing there? Wouldn't it be nice if
|
||||
we had a name for this class of data much like we have string, number,
|
||||
and so on?
|
||||
|
||||
For this situation, Racket provides @deftech{flat named
|
||||
contracts}. The use of ``contract'' in this term shows that contracts
|
||||
are first-class values. The ``flat'' means that the collection of data
|
||||
is a subset of the built-in atomic classes of data; they are described
|
||||
by a predicate that consumes all Racket values and produces a
|
||||
boolean. The ``named'' part says what we want to do, which is to name
|
||||
the contract so that error messages become intelligible:
|
||||
|
||||
@racketmod[
|
||||
racket
|
||||
|
||||
(define (amount? x) (and (number? x) (integer? x) (>= x 0)))
|
||||
(define amount (flat-named-contract 'amount amount?))
|
||||
|
||||
(provide/contract
|
||||
[deposit (amount . -> . any)])
|
||||
|
||||
(define this 0)
|
||||
(define (deposit a) ...)
|
||||
]
|
||||
|
||||
With this little change, the error message becomes all of the
|
||||
sudden quite readable:
|
||||
|
||||
@inset-flow{@racketerror{bank-client broke the contract (-> amount
|
||||
any) it had with myaccount on deposit; expected <amount>, given: -10}}
|
||||
|
||||
|
|
|
@ -3,12 +3,12 @@
|
|||
scribble/eval
|
||||
"guide-utils.ss"
|
||||
"contracts-utils.ss"
|
||||
(for-label scheme/contract))
|
||||
(for-label racket/contract))
|
||||
|
||||
@title[#:tag "contracts-struct"]{Contracts on Structures}
|
||||
|
||||
Modules deal with structures in two ways. First they export
|
||||
@scheme[struct] definitions, i.e., the ability to create
|
||||
@racket[struct] definitions, i.e., the ability to create
|
||||
structs of a certain kind, to access their fields, to modify
|
||||
them, and to distinguish structs of this kind against every
|
||||
other kind of value in the world. Second, on occasion a
|
||||
|
@ -17,12 +17,14 @@ its fields contain values of a certain kind. This section
|
|||
explains how to protect structs with contracts for both
|
||||
uses.
|
||||
|
||||
@ctc-section[#:tag "single-struct"]{Promising Something About a Specific Structure}
|
||||
@; ----------------------------------------------------------------------
|
||||
@ctc-section[#:tag "single-struct"]{Guarantees for a Specific Value}
|
||||
|
||||
Yes. If your module defines a variable to be a structure, then on export you
|
||||
can specify the structures shape:
|
||||
@schememod[
|
||||
scheme
|
||||
If your module defines a variable to be a structure, then you can
|
||||
specify the structure's shape using @racket[struct/c]:
|
||||
|
||||
@racketmod[
|
||||
racket
|
||||
(require lang/posn)
|
||||
|
||||
(define origin (make-posn 0 0))
|
||||
|
@ -32,105 +34,98 @@ scheme
|
|||
]
|
||||
|
||||
In this example, the module imports a library for representing positions, which
|
||||
exports a @scheme[posn] structure. One of the @scheme[posn]s it creates
|
||||
exports a @racket[posn] structure. One of the @racket[posn]s it creates
|
||||
and exports stands for the origin, i.e., @tt{(0,0)}, of the grid.
|
||||
|
||||
@ctc-section[#:tag "single-vector"]{Promising Something About a Specific Vector}
|
||||
@margin-note{See also @racket[vector/c] and similar contract
|
||||
combinators for (flat) compound data.}
|
||||
|
||||
Yes, again. See the help desk for information on @scheme[vector/c] and
|
||||
similar contract combinators for (flat) compound data.
|
||||
@; ----------------------------------------------------------------------
|
||||
@ctc-section[#:tag "define-struct"]{Guarantees for All Values}
|
||||
|
||||
@ctc-section[#:tag "define-struct"]{Ensuring that All Structs are Well-Formed}
|
||||
The book @|HtDP| teaches that @racket[posn]s should contain only
|
||||
numbers in their two fields. With contracts we would enforce this
|
||||
informal data definition as follows:
|
||||
|
||||
The book @link["http://www.htdp.org/"]{@italic{How to Design
|
||||
Programs}} teaches that @scheme[posn]s should contain only
|
||||
numbers in their two fields. With contracts we would enforce
|
||||
this informal data definition as follows:
|
||||
|
||||
@schememod[
|
||||
scheme
|
||||
(define-struct posn (x y))
|
||||
@racketmod[
|
||||
racket
|
||||
(struct posn (x y))
|
||||
|
||||
(provide/contract
|
||||
[struct posn ((x number?) (y number?))]
|
||||
[p-okay posn?]
|
||||
[p-sick posn?])
|
||||
|
||||
(define p-okay (make-posn 10 20))
|
||||
(define p-sick (make-posn 'a 'b))
|
||||
(define p-okay (posn 10 20))
|
||||
(define p-sick (posn 'a 'b))
|
||||
]
|
||||
|
||||
This module exports the entire structure definition: @scheme[make-posn],
|
||||
@scheme[posn?], @scheme[posn-x], @scheme[posn-y],
|
||||
@scheme[set-posn-x!], and @scheme[set-posn-y!]. Each function enforces
|
||||
or promises that the two fields of a @scheme[posn] structure are
|
||||
numbers---when the values flow across the module boundary.
|
||||
This module exports the entire structure definition: @racket[posn],
|
||||
@racket[posn?], @racket[posn-x], @racket[posn-y],
|
||||
@racket[set-posn-x!], and @racket[set-posn-y!]. Each function enforces
|
||||
or promises that the two fields of a @racket[posn] structure are
|
||||
numbers --- when the values flow across the module boundary. Thus, if
|
||||
a client calls @racket[posn] on @racket[10] and @racket['a], the
|
||||
contract system signals a contract violation.
|
||||
|
||||
Thus, if a client calls @scheme[make-posn] on @scheme[10] and
|
||||
@scheme['a], the contract system signals a contract
|
||||
violation.
|
||||
|
||||
The creation of @scheme[p-sick] inside of the @scheme[posn] module,
|
||||
however, does not violate the contracts. The function @scheme[make-posn] is
|
||||
used internally so @scheme['a] and @scheme['b] don't cross the module
|
||||
boundary. Similarly, when @scheme[p-sick] crosses the boundary of
|
||||
@scheme[posn], the contract promises a @scheme[posn?] and nothing
|
||||
else. In particular, this check does @italic{not} require that the fields of
|
||||
@scheme[p-sick] are numbers.
|
||||
The creation of @racket[p-sick] inside of the @racket[posn] module,
|
||||
however, does not violate the contracts. The function @racket[posn] is
|
||||
used internally, so @racket['a] and @racket['b] don't cross the module
|
||||
boundary. Similarly, when @racket[p-sick] crosses the boundary of
|
||||
@racket[posn], the contract promises a @racket[posn?] and nothing
|
||||
else. In particular, this check does @italic{not} require that the
|
||||
fields of @racket[p-sick] are numbers.
|
||||
|
||||
The association of contract checking with module boundaries implies that
|
||||
@scheme[p-okay] and @scheme[p-sick] look alike from a client's
|
||||
@racket[p-okay] and @racket[p-sick] look alike from a client's
|
||||
perspective until the client extracts the pieces:
|
||||
|
||||
@schememod[
|
||||
scheme
|
||||
@racketmod[
|
||||
racket
|
||||
(require lang/posn)
|
||||
|
||||
... (posn-x p-sick) ...
|
||||
]
|
||||
|
||||
Using @scheme[posn-x] is the only way the client can find out what
|
||||
a @scheme[posn] contains in the @scheme[x] field. The application of
|
||||
@scheme[posn-x] sends @scheme[p-sick] back into the
|
||||
@scheme[posn] module and the result value -- @scheme['a] here -- back to
|
||||
Using @racket[posn-x] is the only way the client can find out what
|
||||
a @racket[posn] contains in the @racket[x] field. The application of
|
||||
@racket[posn-x] sends @racket[p-sick] back into the
|
||||
@racket[posn] module and the result value -- @racket['a] here -- back to
|
||||
the client, again across the module boundary. At this very point, the contract
|
||||
system discovers that a promise is broken. Specifically, @scheme[posn-x]
|
||||
system discovers that a promise is broken. Specifically, @racket[posn-x]
|
||||
doesn't return a number but a symbol and is therefore blamed.
|
||||
|
||||
This specific example shows that the explanation for a contract violation
|
||||
doesn't always pinpoint the source of the error. The good news is that the
|
||||
error is located in the @scheme[posn] module. The bad news is that the
|
||||
explanation is misleading. Although it is true that @scheme[posn-x]
|
||||
error is located in the @racket[posn] module. The bad news is that the
|
||||
explanation is misleading. Although it is true that @racket[posn-x]
|
||||
produced a symbol instead of a number, it is the fault of the programmer who
|
||||
created a @scheme[posn] from symbols, i.e., the programmer who added
|
||||
created a @racket[posn] from symbols, i.e., the programmer who added
|
||||
|
||||
@schemeblock[
|
||||
(define p-sick (make-posn 'a 'b))
|
||||
@racketblock[
|
||||
(define p-sick (posn 'a 'b))
|
||||
]
|
||||
to the module. So, when you are looking for bugs based on contract violations,
|
||||
keep this example in mind.
|
||||
|
||||
@(exercise) Use your knowledge from the
|
||||
@ctc-link["single-struct"] section on exporting specific
|
||||
structs and change the contract for @scheme[p-sick] so that
|
||||
the error is caught when @scheme[sick] is exported.
|
||||
to the module. So, when you are looking for bugs based on contract
|
||||
violations, keep this example in mind.
|
||||
|
||||
@(solution)
|
||||
If we want to fix the contract for @racket[p-sick] so that the error
|
||||
is caught when @racket[sick] is exported, a single change suffices:
|
||||
|
||||
A single change suffices:
|
||||
|
||||
@schemeblock[
|
||||
@racketblock[
|
||||
(provide/contract
|
||||
...
|
||||
[p-sick (struct/c posn number? number?)])
|
||||
]
|
||||
|
||||
Instead of exporting @scheme[p-sick] as a plain @scheme[posn?], we use a
|
||||
@scheme[struct/c] contract to enforce constraints on its components.
|
||||
That is, instead of exporting @racket[p-sick] as a plain
|
||||
@racket[posn?], we use a @racket[struct/c] contract to enforce
|
||||
constraints on its components.
|
||||
|
||||
@; ----------------------------------------------------------------------
|
||||
@ctc-section[#:tag "lazy-contracts"]{Checking Properties of Data Structures}
|
||||
|
||||
Contracts written using @scheme[struct/c] immediately
|
||||
Contracts written using @racket[struct/c] immediately
|
||||
check the fields of the data structure, but sometimes this
|
||||
can have disastrous effects on the performance of a program
|
||||
that does not, itself, inspect the entire data structure.
|
||||
|
@ -144,12 +139,12 @@ subtree are smaller than the number in the node, and all of
|
|||
the numbers in the right subtree are larger than the number
|
||||
in the node.
|
||||
|
||||
We can implement a search function @scheme[in?] that takes
|
||||
We can implement a search function @racket[in?] that takes
|
||||
advantage of the structure of the binary search tree.
|
||||
@schememod[
|
||||
scheme
|
||||
@racketmod[
|
||||
racket
|
||||
|
||||
(define-struct node (val left right))
|
||||
(struct node (val left right))
|
||||
|
||||
(code:comment "determines if `n' is in the binary search tree `b',")
|
||||
(code:comment "exploiting the binary search tree invariant")
|
||||
|
@ -180,48 +175,47 @@ scheme
|
|||
]
|
||||
|
||||
In a full binary search tree, this means that
|
||||
the @scheme[in?] function only has to explore a
|
||||
the @racket[in?] function only has to explore a
|
||||
logarithmic number of nodes.
|
||||
|
||||
The contract on @scheme[in?] guarantees that its input
|
||||
The contract on @racket[in?] guarantees that its input
|
||||
is a binary search tree. But a little careful thought
|
||||
reveals that this contract defeats the purpose of the binary
|
||||
search tree algorithm. In particular, consider the
|
||||
inner @scheme[cond] in the @scheme[in?]
|
||||
function. This is where the @scheme[in?] function gets
|
||||
inner @racket[cond] in the @racket[in?]
|
||||
function. This is where the @racket[in?] function gets
|
||||
its speed: it avoids searching an entire subtree at each
|
||||
recursive call. Now compare that to the @scheme[bst-between?]
|
||||
function. In the case that it returns @scheme[#t], it
|
||||
recursive call. Now compare that to the @racket[bst-between?]
|
||||
function. In the case that it returns @racket[#t], it
|
||||
traverses the entire tree, meaning that the speedup
|
||||
of @scheme[in?] is lost.
|
||||
of @racket[in?] is lost.
|
||||
|
||||
In order to fix that, we can employ a new strategy for
|
||||
checking the binary search tree contract. In particular, if
|
||||
we only checked the contract on the nodes
|
||||
that @scheme[in?] looks at, we can still guarantee that
|
||||
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 @scheme[define-contract-struct] in place
|
||||
of @scheme[define-struct]. Like @scheme[define-struct],
|
||||
@scheme[define-contract-struct] defines a maker,
|
||||
predicate, and selectors for a new
|
||||
structure. Unlike @scheme[define-struct], it also
|
||||
defines contract combinators, in this
|
||||
case @scheme[node/c] and @scheme[node/dc]. Also unlike
|
||||
@scheme[define-struct], it does not allow mutators, making
|
||||
its structs always immutable.
|
||||
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.
|
||||
|
||||
The @scheme[node/c] function accepts a contract for each
|
||||
The @racket[node/c] function accepts a contract for each
|
||||
field of the struct and returns a contract on the
|
||||
struct. More interestingly, the syntactic
|
||||
form @scheme[node/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
|
||||
the fields depend on the values of other fields. We can use
|
||||
this to define the binary search tree contract:
|
||||
@schememod[
|
||||
scheme
|
||||
|
||||
@racketmod[
|
||||
racket
|
||||
|
||||
(define-contract-struct node (val left right))
|
||||
|
||||
|
@ -245,30 +239,30 @@ scheme
|
|||
[in? (number? bst/c . -> . boolean?)])
|
||||
]
|
||||
|
||||
In general, each use of @scheme[node/dc] must name the
|
||||
In general, each use of @racket[node/dc] must name the
|
||||
fields and then specify contracts for each field. In the
|
||||
above, the @scheme[val] field is a contract that accepts
|
||||
values between @scheme[low] and @scheme[high].
|
||||
The @scheme[left] and @scheme[right] fields are
|
||||
dependent on the value of the @scheme[val] field,
|
||||
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
|
||||
are built by recursive calls to
|
||||
the @scheme[bst-between/c] function. Taken together,
|
||||
the @racket[bst-between/c] function. Taken together,
|
||||
this contract ensures the same thing that
|
||||
the @scheme[bst-between?] function checked in the
|
||||
the @racket[bst-between?] function checked in the
|
||||
original example, but here the checking only happens
|
||||
as @scheme[in?] explores the tree.
|
||||
as @racket[in?] explores the tree.
|
||||
|
||||
Although this contract improves the performance
|
||||
of @scheme[in?], restoring it to the logarithmic
|
||||
of @racket[in?], restoring it to the logarithmic
|
||||
behavior that the contract-less version had, it is still
|
||||
imposes a fairly large constant overhead. So, the contract
|
||||
library also provides @scheme[define-opt/c] that brings
|
||||
library also provides @racket[define-opt/c] that brings
|
||||
down that constant factor by optimizing its body. Its shape
|
||||
is just like the @scheme[define] above. It expects its
|
||||
is just like the @racket[define] above. It expects its
|
||||
body to be a contract and then optimizes that contract.
|
||||
|
||||
@schemeblock[
|
||||
@racketblock[
|
||||
(define-opt/c (bst-between/c low high)
|
||||
(or/c null?
|
||||
(node/dc [val (between/c low high)]
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
@title[#:tag "contracts" #:style 'toc]{Contracts}
|
||||
|
||||
This chapter provides a gentle introduction to PLT Scheme's
|
||||
This chapter provides a gentle introduction to Racket's
|
||||
contract system.
|
||||
|
||||
@refdetails["contracts"]{contracts}
|
||||
|
|
|
@ -28,7 +28,7 @@ A @racket[lambda] form with @math{n} @racket[_arg-id]s accepts
|
|||
]
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section{Declaring a Rest Argument}
|
||||
@section[#:tag "rest-args"]{Declaring a Rest Argument}
|
||||
|
||||
A @racket[lambda] expression can also have the form
|
||||
|
||||
|
@ -207,7 +207,7 @@ remaining by-position arguments.
|
|||
@refdetails["lambda"]{function expressions}
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section{Arity-Sensitive Functions: @racket[case-lambda]}
|
||||
@section[#:tag "case-lambda"]{Arity-Sensitive Functions: @racket[case-lambda]}
|
||||
|
||||
The @racket[case-lambda] form creates a function that can have
|
||||
completely different behaviors depending on the number of arguments
|
||||
|
|
|
@ -28,9 +28,9 @@ evaluator is created using @scheme[make-base-eval]. See also
|
|||
Uses of @scheme[code:comment] and @schemeidfont{code:blank} are
|
||||
stipped from each @scheme[datum] before evaluation.
|
||||
|
||||
If a @scheme[datum] has the form @scheme[(eval:alts #,(svar
|
||||
show-datum) #,(svar eval-datum))], then @svar[show-datum] is typeset,
|
||||
while @svar[eval-datum] is evaluated.}
|
||||
If a @scheme[datum] has the form @scheme[(@#,indexed-scheme[eval:alts]
|
||||
#,(svar show-datum) #,(svar eval-datum))], then @svar[show-datum] is
|
||||
typeset, while @svar[eval-datum] is evaluated.}
|
||||
|
||||
|
||||
@defform*[[(interaction-eval datum)
|
||||
|
|
Loading…
Reference in New Issue
Block a user