diff --git a/collects/scheme/sandbox.ss b/collects/scheme/sandbox.ss index 820e4e972b..bc0a2dfe8c 100644 --- a/collects/scheme/sandbox.ss +++ b/collects/scheme/sandbox.ss @@ -356,18 +356,20 @@ ;; Like a toplevel (eval `(begin ,@exprs)), but the language that is used may ;; not have a begin. (define (eval* exprs) - (if (null? exprs) - (void) - (let ([deftag (default-continuation-prompt-tag)]) - (let loop ([expr (car exprs)] [exprs (cdr exprs)]) - (if (null? exprs) - (eval expr) - (begin - (call-with-continuation-prompt - (lambda () (eval expr)) - deftag - (lambda (x) (abort-current-continuation deftag x))) - (loop (car exprs) (cdr exprs)))))))) + (call-with-continuation-prompt + (lambda () + (if (null? exprs) + (void) + (let ([deftag (default-continuation-prompt-tag)]) + (let loop ([expr (car exprs)] [exprs (cdr exprs)]) + (if (null? exprs) + (eval expr) + (begin + (call-with-continuation-prompt + (lambda () (eval expr)) + deftag + (lambda (x) (abort-current-continuation deftag x))) + (loop (car exprs) (cdr exprs)))))))))) (define (evaluate-program program limits uncovered!) (when uncovered! diff --git a/collects/scribble/eval.ss b/collects/scribble/eval.ss index f73ddabb89..dbde375ce8 100644 --- a/collects/scribble/eval.ss +++ b/collects/scribble/eval.ss @@ -144,10 +144,14 @@ (loop (extract s cdr car) (list (syntax->datum (datum->syntax #f (extract s cdr cdr car)))))] [else - (let ([r (with-handlers ([exn:fail? (lambda (e) - (list (exn-message e) - (get-output ev) - (get-error-output ev)))]) + (let ([r (with-handlers ([(lambda (x) + (not (exn:break? x))) + (lambda (e) + (list (if (exn? e) + (exn-message e) + (format "uncaught exception: ~s" e)) + (get-output ev) + (get-error-output ev)))]) (list (let ([v (do-plain-eval ev s #t)]) (make-reader-graph (copy-value v (make-hasheq)))) (get-output ev) diff --git a/collects/scribble/manual.ss b/collects/scribble/manual.ss index e0d38b137b..6ce9586fb4 100644 --- a/collects/scribble/manual.ss +++ b/collects/scribble/manual.ss @@ -2042,6 +2042,7 @@ (define (bib-entry #:key key #:title title + #:is-book? [is-book? #f] #:author [author #f] #:location [location #f] #:date [date #f] @@ -2055,12 +2056,19 @@ (append (decode-content (list author)) (list ", ")) null) - (list 'ldquo) - (decode-content (list title)) + (if is-book? + null + (list 'ldquo)) + (if is-book? + (list (italic title)) + (decode-content (list title))) (list (if location "," - ".") - 'rdquo) + ".")) + + (if is-book? + null + (list 'rdquo)) (if location (cons " " (append diff --git a/collects/scribblings/guide/contracts-examples.scrbl b/collects/scribblings/guide/contracts-examples.scrbl index 52f7c13c7b..37bfe3b044 100644 --- a/collects/scribblings/guide/contracts-examples.scrbl +++ b/collects/scribblings/guide/contracts-examples.scrbl @@ -6,18 +6,18 @@ (for-label scheme/contract) (for-label scheme/gui)) -@title{Examples} +@title[#:tag "contracts-examples"]{Examples} This section illustrates the current state of PLT Scheme's contract - implementation with a series of examples from Mitchell and McKim's text - book "Design by Contract, by Example" [Addison and Wesley, 2002]. + implementation with a series of examples from @italic{Design by + Contract, by Example} @cite["Mitchell02"]. Mitchell and McKim's principles for design by contract DbC are derived from the 1970s style algebraic specifications. The overall goal of DbC is to specify the constructors of an algebra in terms of its observers. While we reformulate Mitchell and McKim's terminology and we use a mostly applicative, we - retain their terminology of "classes" and "objects": + retain their terminology of ``classes'' and ``objects'': @itemize{ @item{@bold{Separate queries from commands.} diff --git a/collects/scribblings/guide/contracts-general-function.scrbl b/collects/scribblings/guide/contracts-general-function.scrbl index b7c4182b73..d97e596dad 100644 --- a/collects/scribblings/guide/contracts-general-function.scrbl +++ b/collects/scribblings/guide/contracts-general-function.scrbl @@ -7,9 +7,9 @@ (for-label scheme/contract) (for-label scheme/gui)) -@title{Contracts on Functions in General} +@title[#:tag "contracts-general-functions"]{Contracts on Functions in General} -@ctc-section[#:tag "flat-named-contracts"]{Contract error messages that contain ``...''} +@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 @@ -65,7 +65,7 @@ sudden quite readable: @inset-flow{@schemeerror{bank-client broke the contract (-> amount any) it had with myaccount on deposit; expected , given: -10}} -@ctc-section[#:tag "optional"]{Optional arguments} +@ctc-section[#:tag "optional"]{Optional Arguments} Take a look at this excerpt from a string-processing module, inspired by the @link["http://schemecookbook.org"]{Scheme cookbook}: @@ -121,7 +121,7 @@ arguments: @scheme[char?]. } yourself, you need to communicate across boundaries for everything you write. -@ctc-section[#:tag "rest-args"]{Rest arguments} +@ctc-section[#:tag "rest-args"]{Rest Arguments} We all know that @scheme[+] in Beginner Scheme is a function that consumes at least two numbers but, in principle, @@ -161,7 +161,7 @@ rest argument follows @scheme[#:rest] contract demands a list of values; in this specific examples, these values must be number. -@ctc-section[#:tag "keywords"]{Keyword arguments} +@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, @@ -222,7 +222,7 @@ five keyword arguments, one for each of the keywords Also, just like in a function definition, the keywords in the @scheme[->] may appear in any order. -@ctc-section[#:tag "optional-keywords"]{Optional keyword arguments} +@ctc-section[#:tag "optional-keywords"]{Optional Keyword Arguments} Of course, many of the parameters in @scheme[ask-yes-or-no-question] (from the previous question) @@ -259,7 +259,7 @@ sections. In this case, we have mandatory keywords 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} +@ctc-section[#:tag "arrow-d"]{When a Function's Result Depends on its Arguments} Here is an excerpt from an imaginary (pardon the pun) numerics module: @@ -291,7 +291,7 @@ and @scheme[>=/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} +@ctc-section[#:tag "arrow-d-args"]{When Contract Arguments Depend on Each Other} Eventually bank customers want their money back. Hence, a module that implements a bank account must include a method for withdrawing money. Of @@ -396,7 +396,7 @@ 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"]{Ensuring that a Function Properly Modifies State} The @scheme[->d] contract combinator can also ensure that a function only modifies state according to certain @@ -515,7 +515,7 @@ In the case of @scheme[substring1], we also know that the indices numeric constraints on them. } -@ctc-section[#:tag "multiple"]{Multiple result values} +@ctc-section[#:tag "multiple"]{Multiple Result Values} The function @scheme[split] consumes a list of @scheme[char]s and delivers the string that occurs before the first occurrence of @@ -595,7 +595,7 @@ This contract is expensive to check of course. Here is a slightly ] Click on @scheme[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"]{Procedures of Some Fixed, but Statically Unknown Arity} 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 58deeb06da..99d4203628 100644 --- a/collects/scribblings/guide/contracts-gotchas.scrbl +++ b/collects/scribblings/guide/contracts-gotchas.scrbl @@ -5,9 +5,9 @@ "contracts-utils.ss" (for-label scheme/contract)) -@title{Gotchas} +@title[#:tag "contracts-gotchas"]{Gotchas} -@ctc-section{Using @scheme[set!] to assign to variables provided via @scheme[provide/contract]} +@ctc-section{Using @scheme[set!] to Assign to Variables Provided via @scheme[provide/contract]} The contract library assumes that variables exported via @scheme[provide/contract] are not assigned to, but does not enforce diff --git a/collects/scribblings/guide/contracts-intro.scrbl b/collects/scribblings/guide/contracts-intro.scrbl index 588a19abac..c8316b24b3 100644 --- a/collects/scribblings/guide/contracts-intro.scrbl +++ b/collects/scribblings/guide/contracts-intro.scrbl @@ -47,7 +47,7 @@ scheme/base (define amount ...) ] -@ctc-section[#:tag "amount0"]{A first contract violation} +@ctc-section[#:tag "amount0"]{A First Contract Violation} Suppose the creator of @scheme[a] had written @schememod[ @@ -62,7 +62,7 @@ When module @scheme[a] is required, the monitoring system signals a violation of the contract and blame @scheme[a] for breaking its promises. -@ctc-section[#:tag "qamount"]{A subtle contract violation} +@ctc-section[#:tag "qamount"]{A Subtle Contract Violation} Suppose the creator of @scheme[a] had written @schememod[ @@ -184,7 +184,7 @@ the module boundary for a second time. } -@ctc-section[#:tag "obligations"]{Imposing obligations on a module's clients} +@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 diff --git a/collects/scribblings/guide/contracts-simple-function.scrbl b/collects/scribblings/guide/contracts-simple-function.scrbl index ccdb5f04a0..fa3d42c59c 100644 --- a/collects/scribblings/guide/contracts-simple-function.scrbl +++ b/collects/scribblings/guide/contracts-simple-function.scrbl @@ -18,7 +18,7 @@ value back to the ``client'' module. 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} +@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 @@ -96,7 +96,7 @@ 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} +@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 @@ -120,7 +120,7 @@ is really just a short-hand for 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} +@ctc-section[#:tag "own"]{Rolling Your Own Contracts for Function Arguments} The @scheme[deposit] function adds the given number to the value of @scheme[amount]. While the function's contract prevents clients from @@ -177,7 +177,7 @@ scheme Lesson: learn about the built-in contracts in @schememodname[scheme/contract]. -@ctc-section[#:tag "and-or"]{The @scheme[and/c], @scheme[or/c], and @scheme[listof] contract combinators} +@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] ombine contracts and they do what you expect them to do. @@ -209,7 +209,7 @@ value satisfies @scheme[number?] and @scheme[integer?] and Oh, we almost forgot. What do you think @scheme[(listof char?)] means? Hint: it is a contract! -@ctc-section[#:tag "range"]{Restricting the range of a function} +@ctc-section[#:tag "range"]{Restricting the Range of a Function} Consider a utility module for creating strings from banking records: @@ -280,7 +280,7 @@ scheme ] -@ctc-section{The difference between @scheme[any] and @scheme[any/c]} +@ctc-section{The Difference Between @scheme[any] and @scheme[any/c]} The contract @scheme[any/c] accepts any value, and @scheme[any] is a keyword that can appear in the range of diff --git a/collects/scribblings/guide/contracts-structure.scrbl b/collects/scribblings/guide/contracts-structure.scrbl index bea74d979d..6f1e18a180 100644 --- a/collects/scribblings/guide/contracts-structure.scrbl +++ b/collects/scribblings/guide/contracts-structure.scrbl @@ -5,7 +5,7 @@ "contracts-utils.ss" (for-label scheme/contract)) -@title{Contracts on Structures} +@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 @@ -17,7 +17,7 @@ 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 struct} +@ctc-section[#:tag "single-struct"]{Promising Something About a Specific Structure} Yes. If your module defines a variable to be a structure, then on export you can specify the structures shape: @@ -35,12 +35,12 @@ 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 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} +@ctc-section[#:tag "single-vector"]{Promising Something About a Specific Vector} 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"]{Ensuring that all structs are well-formed} +@ctc-section[#:tag "define-struct"]{Ensuring that All Structs are Well-Formed} The book @link["http://www.htdp.org/"]{@italic{How to Design Programs}} teaches that @scheme[posn]s should contain only @@ -128,7 +128,7 @@ A single change suffices: Instead of exporting @scheme[p-sick] as a plain @scheme[posn?], we use a @scheme[struct/c] contract to enforce constraints on its components. -@ctc-section[#:tag "lazy-contracts"]{Checking properties of data structures} +@ctc-section[#:tag "lazy-contracts"]{Checking Properties of Data Structures} Contracts written using @scheme[struct/c] immediately check the fields of the data structure, but sometimes this diff --git a/collects/scribblings/guide/control.scrbl b/collects/scribblings/guide/control.scrbl new file mode 100644 index 0000000000..b1ec99caca --- /dev/null +++ b/collects/scribblings/guide/control.scrbl @@ -0,0 +1,266 @@ +#lang scribble/doc +@(require scribble/manual + scribble/eval + "guide-utils.ss") + +@(define cc-eval (make-base-eval)) + +@title[#:tag "control" #:style 'toc]{Exceptions and Control} + +Scheme provides an especially rich set of control operations---not +only operations for raising and catching exceptions, but also +operations for grabbing and restoring portions of a computation. + +@local-table-of-contents[] + +@; ---------------------------------------- + +@section[#:tag "exns"]{Exceptions} + +Whenever a run-time error occurs, an @deftech{exception} is +raised. Unless the exception is caught, then it is handled by printing +a message associated with the exception, and then escaping from the +computation. + +@interaction[ +(/ 1 0) +(car 17) +] + +To catch an exception, use the @scheme[with-handlers] form: + +@specform[ +(with-handlers ([predicate-expr handler-expr] ...) + body ...+) +]{} + +Each @scheme[_predicate-expr] in a handler determines a kind of +exception that is caught by the @scheme[with-handlers] form, and the +value representing the exception is passed to the handler procedure +produced by @scheme[_handler-expr]. The result of the +@scheme[_handler-expr] is the result of the @scheme[with-handlers] +expression. + +For example, a divide-by-zero error raises an instance of the +@scheme[exn:fail:contract:divide-by-zero] structure type: + +@interaction[ +(with-handlers ([exn:fail:contract:divide-by-zero? + (lambda (exn) +inf.0)]) + (/ 1 0)) +(with-handlers ([exn:fail:contract:divide-by-zero? + (lambda (exn) +inf.0)]) + (car 17)) +] + +The @scheme[error] function is one way to raise your own exception. It +packages an error message and other information into an +@scheme[exn:fail] structure: + +@interaction[ +(error "crash!") +(with-handlers ([exn:fail? (lambda (exn) 'air-bag)]) + (error "crash!")) +] + +The @scheme[exn:fail:contract:divide-by-zero] and @scheme[exn:fail] +structure types are sub-types of the @scheme[exn] structure +type. Exceptions raised by core forms and functions always raise an +instance of @scheme[exn] or one of its sub-types, but an exception +does not have to be represented by a structure. The @scheme[raise] +function lets you raise any value as an exception: + +@interaction[ +(raise 2) +(with-handlers ([(lambda (v) (equal? v 2)) (lambda (v) 'two)]) + (raise 2)) +(with-handlers ([(lambda (v) (equal? v 2)) (lambda (v) 'two)]) + (/ 1 0)) +] + +Multiple @scheme[_predicate-expr]s in a @scheme[with-handlers] form +let you handle different kinds of exceptions in different ways. The +predicates are tried in order, and if none of them match, then the +exception is propagated to enclosing contexts. + +@interaction[ +(define (always-fail n) + (with-handlers ([even? (lambda (v) 'even)] + [positive? (lambda (v) 'positive)]) + (raise n))) +(always-fail 2) +(always-fail 3) +(always-fail -3) +(with-handlers ([negative? (lambda (v) 'negative)]) + (always-fail -3)) +] + +Using @scheme[(lambda (v) #t)] as a predicate captures all exceptions, of course: + +@interaction[ +(with-handlers ([(lambda (v) #t) (lambda (v) 'oops)]) + (car 17)) +] + +Capturing all exceptions is usually a bad idea, however. If the user +types Ctl-C in a terminal window or clicks the @onscreen{Stop} button +in DrScheme to interrupt a computation, then normally the +@scheme[exn:break] exception should not be caught. To catch only +exceptions that represent errors, use @scheme[exn:fail?] as the +predicate: + +@interaction[ +(with-handlers ([exn:fail? (lambda (v) 'oops)]) + (car 17)) +(eval:alts ; `examples' doesn't catch break exceptions! + (with-handlers ([exn:fail? (lambda (v) 'oops)]) + (break-thread (current-thread)) (code:comment #, @t{simulate Ctl-C}) + (car 17)) + (error "user break")) +] + +@; ---------------------------------------- + +@section[#:tag "prompt"]{Prompts and Aborts} + +When an exception is raised control, escapes out of an arbitrary deep +evaluation context to the point where the exception is caught---or all +the way out if the expression is never caught: + +@interaction[ +(+ 1 (+ 1 (+ 1 (+ 1 (+ 1 (+ 1 (/ 1 0))))))) +] + +But if control escapes ``all the way out,'' why does the @tech{REPL} +keep going after an error is printed? You might think that it's +because the @tech{REPL} wraps every interaction in a +@scheme[with-handlers] form that catches all exceptions, but that's +not quite the reason. + +The actual reason is that the @tech{REPL} wraps the interaction with a +@deftech{prompt}, which effectively marks the evaluation context with +an escape point. If an exception is not caught, then information about +the exception is printed, and then evaluation @deftech{aborts} to the +nearest enclosing prompt. More precisely, each prompt has a +@deftech{prompt tag}, and there is a designated @deftech{default +prompt tag} that the uncaught-exception handler uses to @tech{abort}. + +The @scheme[call-with-continuation-prompt] function installs a prompt +with a given @tech{prompt tag}, and then it evaluates a given thunk +under the prompt. The @scheme[default-continuation-prompt-tag] +function returns the @tech{default prompt tag}. The +@scheme[abort-current-continuation] function escapes to the nearest +enclosing prompt that has a given @tech{prompt tag}. + +@interaction[ +(define (escape v) + (abort-current-continuation + (default-continuation-prompt-tag) + (lambda () v))) +(+ 1 (+ 1 (+ 1 (+ 1 (+ 1 (+ 1 (escape 0))))))) +(+ 1 + (call-with-continuation-prompt + (lambda () + (+ 1 (+ 1 (+ 1 (+ 1 (+ 1 (+ 1 (escape 0)))))))) + (default-continuation-prompt-tag))) +] + +In @scheme[escape] above, the value @scheme[v] is wrapped in a +procedure that is called after escaping to the enclosing prompt. + +@tech{Prompts} and @tech{aborts} look very much like exception +handling and raising. Indeed, prompts and aborts are essentially a +more primitive form of exceptions, and @scheme[with-handlers] and +@scheme[raise] are implemented in terms of prompts and aborts. The +power of the more primitive forms is related to the word +``continuation'' in the operator names, as we discuss in the next +section. + +@; ---------------------------------------------------------------------- + +@section{Continuations} + + +A @deftech{continuation} is a value that encapsulates a piece of an +expression context. The @scheme[call-with-composable-continuation] +function captures the @deftech{current continuation} starting outside +function call an running up to the nearest enclosing prompt. (Keep in +mind that each @tech{REPL} interaction is implicitly wrapped in a +prompt.) + +For example, in + +@schemeblock[ +(+ 1 (+ 1 (+ 1 0))) +] + +at the point where @scheme[0] is evaluated, the expression context +includes three nested addition expressions. We can grab that context by +changing @scheme[0] to grab the continuation before returning 0: + +@interaction[ +#:eval cc-eval +(define saved-k #f) +(define (save-it!) + (call-with-composable-continuation + (lambda (k) (code:comment #, @t{@scheme[k] is the captured continuation}) + (set! saved-k k) + 0))) +(+ 1 (+ 1 (+ 1 (save-it!)))) +] + +The @tech{continuation} saved in @scheme[save-k] encapsulates the +program context @scheme[(+ 1 (+ 1 (+ 1 _?)))], where @scheme[_?] +represents a place to plug in a result value---because that was the +expression context when @scheme[save-it!] was called. The +@tech{continuation} is encapsulated so that it behaves like the +function @scheme[(lambda (v) (+ 1 (+ 1 (+ 1 v))))]: + +@interaction[ +#:eval cc-eval +(saved-k 0) +(saved-k 10) +(saved-k (saved-k 0)) +] + +The continuation captured by +@scheme[call-with-composable-continuation] is determined dynamically, +not syntactically. For example, with + +@interaction[ +#:eval cc-eval +(define (sum n) + (if (zero? n) + (save-it!) + (+ n (sum (sub1 n))))) +(sum 5) +] + +the continuation in @scheme[saved-k] becomes @scheme[(lambda (x) (+ 5 +(+ 4 (+ 3 (+ 2 (+ 1 x))))))]: + +@interaction[ +#:eval cc-eval +(saved-k 0) +(saved-k 10) +] + +A more traditional continuation operator in Scheme is +@scheme[call-with-current-continuation], which is often abbreviated +@scheme[call/cc]. It is like +@scheme[call-with-composable-continuation], but applying the captured +continuation first @tech{aborts} (to the current @tech{prompt}) before +restoring the saved continuation. In addition, Scheme systems +traditionally support a single prompt at the program start, instead of +allowing new prompts via +@scheme[call-with-continuation-prompt]. Continuations as in PLT Scheme +are sometimes called @deftech{delimited continuations}, since a +program can introduce new delimiting prompts, and continuations as +captured by @scheme[call-with-composable-continuation] are sometimes +called @deftech{composable continuations}, because they do not have a +built-in @tech{abort}. + +For an example of how @tech{continuations} are useful, see +@other-manual['(lib "scribblings/more/more.scrbl")]. For specific +control operators that have more convenient names than the primitives +described here, see @schememodname[scheme/control]. diff --git a/collects/scribblings/guide/guide.scrbl b/collects/scribblings/guide/guide.scrbl index 691318c7b3..eb37d08b49 100644 --- a/collects/scribblings/guide/guide.scrbl +++ b/collects/scribblings/guide/guide.scrbl @@ -36,17 +36,12 @@ precise details to @|MzScheme| and other reference manuals. @include-section["regexp.scrbl"] -@; ---------------------------------------------------------------------- -@section[#:tag "control"]{Exceptions and Control} +@include-section["control.scrbl"] -@; ---------------------------------------------------------------------- @include-section["for.scrbl"] - -@; ---------------------------------------------------------------------- @include-section["match.scrbl"] -@; ---------------------------------------------------------------------- @include-section["class.scrbl"] @; ---------------------------------------------------------------------- @@ -145,7 +140,12 @@ downloadable packages contributed by PLT Scheme users. #:title "Scheme with Classes, Mixins, and Traits (invited tutorial)" #:location "Asian Symposium on Programming Languages and Systems" #:date "2006") - + + (bib-entry #:key "Mitchell02" + #:author "Richard Mitchell and Jim McKim" + #:title "Design by Contract, by Example" + #:is-book? #t + #:date "2002") ) diff --git a/collects/scribblings/guide/match.scrbl b/collects/scribblings/guide/match.scrbl index 2dcd24f5c5..42e412a134 100644 --- a/collects/scribblings/guide/match.scrbl +++ b/collects/scribblings/guide/match.scrbl @@ -4,9 +4,119 @@ "guide-utils.ss" (for-label scheme/match)) +@(begin + (define match-eval (make-base-eval)) + (interaction-eval #:eval match-eval (require scheme/match))) + @title[#:tag "match"]{Pattern Matching} The @scheme[match] form supports pattern matching on arbitrary Scheme values, as opposed to functions like @scheme[regexp-match] that compare regular expressions to byte and character sequences (see @secref["regexp"]). + +@specform[ +(match target-expr + [pattern expr ...+] ...) +] + +The @scheme[match] form takes the result of @scheme[target-expr] and +tries to match each @scheme[_pattern] in order. As soon as it finds a +match, it evaluates the corresponding @scheme[_expr] sequence to +obtain the result for the @scheme[match] form. If @scheme[_pattern] +includes @deftech{pattern variables}, they are treated like wildcards, +and each variable is bound in the @scheme[_expr] to the input +fragments that it matched. + +Most Scheme literal expressions can be used as patterns: + +@interaction[ +#:eval match-eval +(match 2 + [1 'one] + [2 'two] + [3 'three]) +(match #f + [#t 'yes] + [#f 'no]) +(match "apple" + ['apple 'symbol] + ["apple" 'string] + [#f 'boolean]) +] + +Constructors like @scheme[cons], @scheme[list], and @scheme[vector] +can be used to create patterns that match pairs, lists, and vectors: + +@interaction[ +#:eval match-eval +(match '(1 2) + [(list 0 1) 'one] + [(list 1 2) 'two]) +(match '(1 . 2) + [(list 1 2) 'list] + [(cons 1 2) 'pair]) +(match #(1 2) + [(list 1 2) 'list] + [(vector 1 2) 'vector]) +] + +The @scheme[struct] construct matches an instance of a particular +structure type: + +@interaction[ +#:eval match-eval +(define-struct shoe (size color)) +(define-struct hat (size style)) +(match (make-hat 23 'bowler) + [(struct shoe (10 'white)) "bottom"] + [(struct hat (23 'bowler)) "top"]) +] + +Unquoted, non-constructor identifiers in an pattern are @tech{pattern +variables} that are bound in the result expressions: + +@interaction[ +#:eval match-eval +(match '(1) + [(list x) (+ x 1)] + [(list x y) (+ x y)]) +(match '(1 2) + [(list x) (+ x 1)] + [(list x y) (+ x y)]) +(match (make-hat 23 'bowler) + [(struct shoe (sz col)) sz] + [(struct hat (sz stl)) sz]) +] + +An ellipsis, written @litchar{...}, act like a Kleene star within a +list or vector pattern: the preceding sub-pattern can be used to match +any number of times for any number of consecutive elements of the list +of vector. If a sub-pattern followed by an ellipsis includes a pattern +variable, the variable matches multiple times, and it is bound in the +result expression to a list of matches: + +@interaction[ +#:eval match-eval +(match '(1 1 1) + [(list 1 ...) 'ones] + [else 'other]) +(match '(1 1 2) + [(list 1 ...) 'ones] + [else 'other]) +(match '(1 2 3 4) + [(list 1 x ... 4) x]) +(match (list (make-hat 23 'bowler) (make-hat 22 'pork-pie)) + [(list (struct hat (sz styl)) ...) (apply + sz)]) +] + +Ellipses can be nested to match nested repetitions, and in that case, +pattern variables can be bound to lists of lists of matches: + +@interaction[ +#:eval match-eval +(match '((! 1) (! 2 2) (! 3 3 3)) + [(list (list '! x ...) ...) x]) +] + +For information on many more pattern forms, see @schememodname[scheme/match]. diff --git a/collects/scribblings/scribble/manual.scrbl b/collects/scribblings/scribble/manual.scrbl index ca596fd048..a69ccd7ba0 100644 --- a/collects/scribblings/scribble/manual.scrbl +++ b/collects/scribblings/scribble/manual.scrbl @@ -928,6 +928,7 @@ order as given} @defproc[(bib-entry [#:key key string?] [#:title title any/c] + [#:is-book? is-book? any/c #f] [#:author author any/c] [#:location location any/c] [#:date date any/c] @@ -941,7 +942,8 @@ the entry: @itemize{ @item{@scheme[title] is the title of the cited work. It will be - surrounded by quotes in typeset form.} + surrounded by quotes in typeset form if @scheme[is-book?] is + @scheme[#f], otherwise it is typeset via @scheme[italic].} @item{@scheme[author] lists the authors. Use names in their usual order (as opposed to ``last, first''), and separate multiple