diff --git a/collects/scribblings/guide/contracts-examples.scrbl b/collects/scribblings/guide/contracts-examples.scrbl index 68a517d711..48a0b51613 100644 --- a/collects/scribblings/guide/contracts-examples.scrbl +++ b/collects/scribblings/guide/contracts-examples.scrbl @@ -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. diff --git a/collects/scribblings/guide/contracts-exists.scrbl b/collects/scribblings/guide/contracts-exists.scrbl index 4447f3f9d3..c2014841db 100644 --- a/collects/scribblings/guide/contracts-exists.scrbl +++ b/collects/scribblings/guide/contracts-exists.scrbl @@ -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"]. diff --git a/collects/scribblings/guide/contracts-general-function.scrbl b/collects/scribblings/guide/contracts-general-function.scrbl index 37ea2969da..653e8d99d9 100644 --- a/collects/scribblings/guide/contracts-general-function.scrbl +++ b/collects/scribblings/guide/contracts-general-function.scrbl @@ -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 , 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: + +(provide/contract + [substring1 (case-> + (string? . -> . string?) + (->r ([s string?] + [_ (and/c natural-number/c (r ([s string?] + [a (and/c natural-number/c (=/c a) + ( + 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: - -(provide/contract - [substring1 (case-> - (string? . -> . string?) - (->r ([s string?] - [_ (and/c natural-number/c (r ([s string?] - [a (and/c natural-number/c (=/c a) - ( - 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 diff --git a/collects/scribblings/guide/contracts-gotchas.scrbl b/collects/scribblings/guide/contracts-gotchas.scrbl index de84d5ad48..8fa34345c8 100644 --- a/collects/scribblings/guide/contracts-gotchas.scrbl +++ b/collects/scribblings/guide/contracts-gotchas.scrbl @@ -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. diff --git a/collects/scribblings/guide/contracts-intro.scrbl b/collects/scribblings/guide/contracts-intro.scrbl index 84bf05e6ac..ddeaf3b0eb 100644 --- a/collects/scribblings/guide/contracts-intro.scrbl +++ b/collects/scribblings/guide/contracts-intro.scrbl @@ -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?]) - + [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?]) - + [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.
- + ;; 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) (set! amount -4))) -(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) - +
 
 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)
 
 

Note: 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 not 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 not 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. } +@;{ + @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. + diff --git a/collects/scribblings/guide/contracts-simple-function.scrbl b/collects/scribblings/guide/contracts-simple-function.scrbl index 23e8381d0b..128d5988ec 100644 --- a/collects/scribblings/guide/contracts-simple-function.scrbl +++ b/collects/scribblings/guide/contracts-simple-function.scrbl @@ -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. -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[ +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 + +(provide/contract + [deposit (-> number? any)] + [balance (-> number?)]) + +(define amount 0) +(define (deposit a) (set! amount (+ amount a))) +(define (balance) amount) +] + +The module exports two functions: + +@itemize[ + +@item{@racket[deposit], which accepts a number and returns some value + that is not specified in the contract, and} + +@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?)]) -] -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.'' - -@ctc-section[#:tag "dots"]{Infix Contract Notation} - -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: - -@schemeblock[ -(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?)] - + .... (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 , given: -10}} diff --git a/collects/scribblings/guide/contracts-structure.scrbl b/collects/scribblings/guide/contracts-structure.scrbl index 21a56763a3..87ecd8d374 100644 --- a/collects/scribblings/guide/contracts-structure.scrbl +++ b/collects/scribblings/guide/contracts-structure.scrbl @@ -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)] diff --git a/collects/scribblings/guide/contracts.scrbl b/collects/scribblings/guide/contracts.scrbl index 27c0851f4b..fd6fee40e5 100644 --- a/collects/scribblings/guide/contracts.scrbl +++ b/collects/scribblings/guide/contracts.scrbl @@ -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} diff --git a/collects/scribblings/guide/lambda.scrbl b/collects/scribblings/guide/lambda.scrbl index 1b6636cef5..625e74e12d 100644 --- a/collects/scribblings/guide/lambda.scrbl +++ b/collects/scribblings/guide/lambda.scrbl @@ -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 diff --git a/collects/scribblings/scribble/eval.scrbl b/collects/scribblings/scribble/eval.scrbl index ab93e2af00..88504396c9 100644 --- a/collects/scribblings/scribble/eval.scrbl +++ b/collects/scribblings/scribble/eval.scrbl @@ -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)