fill in some guide sections
svn: r9871
This commit is contained in:
parent
61a80e85fb
commit
2b48cb0a4d
|
@ -356,6 +356,8 @@
|
|||
;; Like a toplevel (eval `(begin ,@exprs)), but the language that is used may
|
||||
;; not have a begin.
|
||||
(define (eval* exprs)
|
||||
(call-with-continuation-prompt
|
||||
(lambda ()
|
||||
(if (null? exprs)
|
||||
(void)
|
||||
(let ([deftag (default-continuation-prompt-tag)])
|
||||
|
@ -367,7 +369,7 @@
|
|||
(lambda () (eval expr))
|
||||
deftag
|
||||
(lambda (x) (abort-current-continuation deftag x)))
|
||||
(loop (car exprs) (cdr exprs))))))))
|
||||
(loop (car exprs) (cdr exprs))))))))))
|
||||
|
||||
(define (evaluate-program program limits uncovered!)
|
||||
(when uncovered!
|
||||
|
|
|
@ -144,8 +144,12 @@
|
|||
(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)
|
||||
(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)])
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.}
|
||||
|
|
|
@ -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 <amount>, 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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
|||
</question>
|
||||
}
|
||||
|
||||
@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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
266
collects/scribblings/guide/control.scrbl
Normal file
266
collects/scribblings/guide/control.scrbl
Normal file
|
@ -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].
|
|
@ -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"]
|
||||
|
||||
@; ----------------------------------------------------------------------
|
||||
|
@ -146,6 +141,11 @@ downloadable packages contributed by PLT Scheme users.
|
|||
#: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")
|
||||
|
||||
)
|
||||
|
||||
|
|
|
@ -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].
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue
Block a user