Exceptions and Control Flow
6.1 Exceptions
MzScheme supports the exception system proposed by Friedman, Haynes, and Dybvig.13 MzScheme's implementation extends that proposal by defining the specific exception values that are raised by each primitive error.
(raise
v
)
raises an exception, wherev
represents the exception being raised. Thev
argument can be anything; it is passed to the current exception handler. Breaks are disabled from the time the exception is raised until the exception handler obtains control, and the handler itself isparameterize-break
ed to disable breaks initially; see section 6.7 for more information on breaks.(current-exception-handler)
returns the current exception handler that is used by
, andraise
(current-exception-handler
installs the proceduref
)f
as the current exception handler. The
procedure is a parameter (see section 7.9).current-exception-handler
Any procedure that takes one argument can be an exception handler, but it is an error if the exception handler returns to its caller when invoked by
. If an exception handler returns, the current error display handler and current error escape handler are called directly to report the handler's mistake. Furthermore, the call to an exception handler israise
parameterize
d to setcurrent-exception-handler
to the default exception handler, and it isparameterize-break
ed to disable breaks.The default exception handler prints an error message using the current error display handler (see
in section 7.9.1.7) and then escapes by calling the current error escape handler (seeerror-display-handler
in section 7.9.1.7). The call to each handler iserror-escape-handler
parameterize
d to seterror-display-handler
to the default error display handler,14 and it isparameterize-break
ed to disable breaks. The call to the error escape handler is further parameterized to seterror-escape-handler
to the default error escape handler.(
is a syntactic form that evaluates thewith-handlers
((pred
handler
) ···)expr
···1)expr
body, installing a new exception handler before evaluating theexpr
s and restoring the handler when a value is returned (or when control escapes from the expression). Thepred
andhandler
expressions are evaluated in the order that they are specified, before the firstexpr
and before the exception handler is changed. The exception handler is installed and restored withparameterize
(see section 7.9.2).The new exception handler processes an exception only if one of the
pred
procedures returns a true value when applied to the exception, otherwise the original exception handler is invoked (by raising the exception again). If an exception is handled by one of thehandler
procedures, the result of the entirewith-handlers
expression is the return value of the handler.When an exception is raised during the evaluation of
expr
s, each predicate procedurepred
is applied to the exception value; if a predicate returns a true value, the correspondinghandler
procedure is invoked with the exception as an argument. The predicates are tried in the order that they are specified.Before any predicate or handler procedure is invoked, the continuation of the entire
with-handlers
expression is restored, but alsoparameterize-break
ed to disable breaks. Thus, breaks are disabled by default during the predicate and handler procedures (see section 6.7), and the ``original'' exception handler is active (i.e., the one present before thewith-handlers
expression was evaluated).The
procedure is useful as a handler predicate to catch all error exceptions. Avoid usingexn:fail?
(lambda (x) #t)
as a predicate, because theexn:break
exception typically should not be caught (unless it will be re-raised to cooperatively break). Beware, also, of catching and discarding exceptions, because discarding an error message can make debugging unnecessarily difficult.(
is the same aswith-handlers*
((pred
handler
) ···)expr
···1)with-handlers
, but if ahandler
procedure is called, breaks are not explicitly disabled, and the call is in tail position with respect to thewith-handlers*
form.
The following example defines a divide procedure that returns
+inf.0
when dividing by zero instead of signaling an exception
(other exceptions raised by /
are signaled):
(define div-w-inf
(lambda (n d)
(with-handlers ([exn:fail:contract:divide-by-zero?
(lambda (exn) +inf.0)])
(/ n d))))
The following example catches and ignores file exceptions, but lets the enclosing context handle breaks:
(define (file-date-if-there filename) (with-handlers ([exn:fail:filesystem?
(lambda (exn) #f)]) (file-or-directory-modify-seconds
filename)))
6.1.1 Primitive Exceptions
Whenever a primitive error occurs in MzScheme, an exception is raised.
The value that is passed to the current exception handler is always
an instance of the exn
structure type. Every exn
structure value has a message
field that is a string, the
primitive error message. The default exception handler recognizes
exception values with the exn?
predicate and passes the
error message to the current error display handler (see
in section 7.9.1.7).error-display-handler
Primitive errors do not create immediate instances of the exn
structure type. Instead, an instance from a hierarchy of subtypes of
exn
is instantiated. The subtype more precisely identifies the
error that occurred and may contain additional information about the
error. The table below defines the type hierarchy that is used by
primitive errors and matches each subtype with the primitive errors
that instantiate it.
In the table, each bulleted line is a separate structure type. A type
is nested under another when it is a subtype.
For example, reading an ill-formed expression raises an exception as
an instance of exn:fail:read
. An exception handler can test
for this kind of exception using the global exn:fail:read?
predicate. Given such an exception, an error string can be extracted
using
, while exn-message
exn:fail:read-source
accesses a list of source locations for the error.
Fields of the built-in exn
structure types are immutable, so
field mutators are not provided. Field-type contracts are enforced
through guards; for example, (
raises
make-exn
"Hello" #f)exn:fail:contract
because the second argument is not a
continuation mark set. All built-in exn
structure types are
transparent to all inspectors (see section 4.5).
exn
: not instantiated directlymessage
field,immutable string
-- error messagecontinuation-marks
field,mark set
-- value returned bycurrent-continuation-marks
immediately before the exception is raised
exn:fail:read
:read
parsing errorsrclocs
field,immutable list of
-- source location(s) of errorsrcloc
s (see section 11.2.1.1)
exn:break
: asynchronous break signalcontinuation
field,escape continuation
-- resumes from the break
In addition to the built-in structure types for exceptions, MzScheme provides one built-in structure-type property (see section 4.4):
The
prop:exn:srclocs
property identifies exceptions that have a list of source locations, which includesexn:fail:read
andexn:fail:syntax
. Theexn:srclocs?
predicate recognizes structures and structure types that have theprop:exn:srclocs
property. Theexn:srclocs-accessor
procedure takes a structure or structure type with theexn:srclocs
property and returns a procedure; when the procedure is applied to a structure of the same type, it returns a list ofsrcloc
s (see section 11.2.1.1).
Primitive procedures that accept a procedure argument with a
particular required arity (e.g.,
,
call-with-input-file
) check the argument's arity immediately, raising
call/cc
exn:fail:contract
if the arity is incorrect.
6.2 Errors
The procedure
raises the exception error
exn:fail
(which contains an error string). The
procedure has
three forms:
error
(error
symbol
)
creates a message string by concatenating"error: "
with the string form ofsymbol
.(error
msg-string v
···)
creates a message string by concatenatingmsg-string
with string versions of thev
s (as produced by the current error value conversion handler; see section 7.9.1.7). A space is inserted before eachv
.(error
src-symbol format-string v
···)
creates a message string equivalent to the string created by:(
format
(string-append
"~s: " format-string) src-symbol v ···)
In all cases, the constructed message string is passed to
make-exn:fail
and the resulting exception is raised.
The raise-user-error
procedure is the same as error
,
except that it constructs an exception
with make-exn:fail:user
instead
of make-exn:fail
. The default error display handler does not
show a ``stack trace'' for exn:fail:user
exceptions
(see section 6.6), so raise-user-error
should be used
for errors that are intended for end
users. Like error
, raise-user-error
has three forms:
6.2.1 Application Errors
(raise-type-error
name-symbol expected-string v
)
creates an exn:fail:contract
value and
s it as an
exception. The raise
name-symbol
argument is used as the source
procedure's name in the error message. The expected-string
argument is used as a description of the expected type, and v
is the value received by the procedure that does not have the
expected type.
(raise-type-error
name-symbol expected-string bad-k v
)
is similar, except that the bad argument is indicated by an index
(from 0), and all of the original arguments v
are provided
(in order). The resulting error message names the bad argument and
also lists the other arguments. If bad-k
is not less than the
number of v
s, the exn:fail:contract
exception is raised.
(raise-mismatch-error
name-symbol message-string v
)
creates an
exn:fail:contract
value and
s it as an
exception. The raise
name-symbol
is used as the source procedure's
name in the error message. The message-string
is the error
message. The v
argument is the improper argument received by
the procedure. The printed form of v
is appended to
message-string
(using the error value conversion handler; see
section 7.9.1.7).
(raise-arity-error
name-symbol-or-procedure arity-v
[arg-v
···])
creates an
exn:fail:contract:arity
value and
s it as an
exception. The raise
name-symbol-or-procedure
is used for the source procedure's
name in the error message. The arity-v
value must be a possible
result from procedure-arity
(see section 3.12.1), and it is
used for the procedure's arity in the error message; if
name-symbol-or-procedure
is a procedure, its actual arity is ignored.
The arg-v
arguments are the actual supplied arguments, which are
shown in the error message (using the error value conversion handler; see
section 7.9.1.7); also, the number of
supplied arg-v
s is explicitly mentioned in the message.
6.2.2 Syntax Errors
(raise-syntax-error
name message-string
[expr sub-expr
])
creates
an exn:fail:syntax
value and
s it as an exception.
Macros use this procedure to report syntax errors. The raise
name
argument is usually #f
when expr
is provided; it is
described in more detail below. The message-string
is used as
the main body of the error message. The optional expr
argument
is the erroneous source syntax object or S-expression. The optional
sub-expr
argument is a syntax object or S-expression within
expr
that more precisely locates the error. If sub-expr
is provided, it is used (in syntax form) for the exprs
field
of the generated exception record, else the expr
is used if
provided, otherwise the exprs
field is the empty
list. Source location information in the error-message text is
similarly extracted from sub-expr
or expr
, when at least
one is a syntax object.
The form name used in the generated error message is determined
through a combination of the name
, expr
, and
sub-expr
arguments. The name
argument can #f
or
a symbol:
#f
: Whenname
is#f
, and whenexpr
is either an identifier or a syntax pair containing an identifier as its first element, then the form name from the error message is the identifier's symbol.If
expr
is not provided, or if it is not an identifier or a syntax pair containing and identifier as its first element, then the form name in the error message is"?"
.symbol
: Whenname
is a symbol, then the symbol is used as the form name in the generated error message.
See also section 7.9.1.7.
6.2.3 Inferred Value Names
To improve error reporting, names are inferred at compile-time for certain kinds of values, such as procedures. For example, evaluating the following expression:
(let ([f (lambda () 0)]) (f 1 2 3))
produces an error message because too many arguments are provided to
the procedure. The error message is able to report ``f'' as the name
of the procedure. In this case, MzScheme decides, at compile-time, to
name as f
all procedures created by the let
-bound
lambda
.
Names are inferred whenever possible for procedures. Names closer to an expression take precedence. For example, in
(define my-f (let ([f (lambda () 0)]) f))
the procedure bound to my-f
will have the inferred name ``f''.
When an 'inferred-name
property is attached to a syntax
object for an expression (see section 12.6.2), the property value
is used for naming the expression, and it overrides any name that was
inferred from the expression's context.
When an inferred name is not available, but a source location is
available, a name is constructed using the source location
information. Inferred and property-assigned names are also available
to syntax transformers, via syntax-local-name
; see
section 12.6 for more information.
(object-name
v
)
returns a value for the name of v
if
v
has a name, #f
otherwise. The argument v
can
be any value, but only (some) procedures, structs, struct types,
struct type properties, regexp values, and ports have names. The name
of a procedure, struct, struct type, or struct type property is
always a symbol. The name of a regexp value is a string, and a
byte-regexp value's name is a byte string. The name of a port is
typically a path or a string, but it can be arbitrary. All primitive
procedures have names (see section 3.12.2).
6.3 Continuations
MzScheme supports delimited continuations, and even continuations
captured by
(or
call-with-current-continuation
call/cc
) are delimited by a prompt. Prompt instances
are tagged, and continuations are captured with respect to a particular
prompt. Thus, MzScheme's
accepts an optional prompt-tag argument:
call-with-current-continuation
(call-with-current-continuation
proc
[prompt-tag
])
, where
prompt-tag
must be a result from either
(the default)
or default-continuation-prompt-tag
. Prompts, prompt tags, and
composable continuations are described further in section 6.5.make-continuation-prompt-tag
The macro let/cc
binds a variable to the continuation in
an immediate body of expressions:
(let/cc k expr ···1)
=expands=>
(call/cc
(lambda (k) expr ···1))
Capturing a continuation also captures the current continuation marks
(see section 6.6) up to the relevant prompt. The current
parameterization (see section 7.9) is captured if it was extended
via paramaterize
or installed via call-with-parameterization
since the prompt.
A continuation can be invoked from the thread (see Chapter 7) other than the one where it was captured. Multiple return values can be passed to a continuation (see section 2.2).
MzScheme installs a continuation barrier around evaluation in the following contexts, preventing full-continuation jumps across the barrier:
applying an exception handler, an error escape handler, or an error display handler (see section 6.1);
applying a macro transformer (see section 12.6), evaluating a compile-time expression, or applying a module name resolver (see section 5.4.1);
applying a custom-port procedure (see section 11.1.7), an event guard procedure (see section 7.7), or a parameter guard procedure (see section 7.9);
applying a security-guard procedure (see section 9.1);
applying a will procedure (see section 13.3); or
evaluating or loading code from the stand-alone MzScheme command line (see section 17).
In addition, extensions of MzScheme may install barriers in additional
contexts. In particular, MrEd installs a continuation barrier around
most every callback. Finally,
(call-with-continuation-barrier
thunk
)
applies thunk
with a
barrier between the application and the current continuation.
In addition to regular
, MzScheme
provides call/cc
call-with-escape-continuation
(or call/ec
) and let/ec
. A
continuation obtained from
is actually a kind of
prompt: applying an escape continuation can only
escape back to the continuation (possibly past a continuation
barrier); that is, an escape continuation
is only valid when the current continuation is an extension of the
escape continuation. Further, the application of call/ec
's argument
is not a tail call. Escape continuations are provided mainly for
backward compatibility, since they pre-date general prompts in MzScheme.call/ec
The exn:fail:contract:continuation
exception is raised when a continuation
application would cross a continuation barrier, or when an escape
continuation is applied outside of its dynamic scope.
6.4 Dynamic Wind
(dynamic-wind
pre-thunk value-thunk post-thunk
)
applies its three
thunk arguments in order. The value of a
expression is the value returned by dynamic-wind
value-thunk
. The
pre-thunk
procedure is invoked before calling value-thunk
and post-thunk
is invoked after value-thunk
returns. The
special properties of
are manifest when control
jumps into or out of the dynamic-wind
value-thunk
application (either due to
a prompt abort or a continuation invocation): every time control jumps
into the value-thunk
application, pre-thunk
is invoked,
and every time control jumps out of value-thunk
,
post-thunk
is invoked. (No special handling is performed for
jumps into or out of the pre-thunk
and post-thunk
applications.)
When
calls dynamic-wind
pre-thunk
for normal evaluation of
value-thunk
, the continuation of the pre-thunk
application calls value-thunk
(with
's
special jump handling) and then dynamic-wind
post-thunk
. Similarly,
the continuation of the post-thunk
application returns the
value of the preceding value-thunk
application to the
continuation of the entire
application.dynamic-wind
When pre-thunk
is called due to a continuation jump, the
continuation of pre-thunk
jumps to a more deeply nested
pre-thunk
, if any, or jumps to the destination continuation; thencontinues with the context of the
pre-thunk
's
call.dynamic-wind
Normally, the second part of this continuation is never reached, due
to a jump in the first part. However, the second part is relevant
because it enables jumps to escape continuations that are contained
in the context of the
call. Furthermore, it
means that the continuation marks (see section 6.6) and
parameterization (see section 7.9) for dynamic-wind
pre-thunk
correspond to those of the dynamic-wind
call that installed
pre-thunk
. The pre-thunk
call, however, is
parameterize-break
ed to disable breaks (see also
section 6.7).
Similarly, when post-thunk
is called due to a continuation jump,
the continuation of post-thunk
jumps to a less deeply nested
post-thunk
, if any, or jumps to a pre-thunk
protecting
the destination, if any, or jumps to the destination continuation,
then continues from the post-thunk
's
application. As for dynamic-wind
pre-thunk
, the parameterization of the
original dynamic-wind
call is restored for the call, and the
call is parameterize-break
ed to disable breaks.
Example:
(let ([v (let/ec out (dynamic-wind
(lambda () (display
"in ")) (lambda () (display
"pre ") (display
(call/cc
out)) #f) (lambda () (display
"out "))))]) (when v (v "post "))) ; =>s
display
in pre out in post out
(let/ec k0 (let/ec k1 (dynamic-wind
void
(lambda () (k0 'cancel)) (lambda () (k1 'cancel-canceled))))) ; =>'cancel-canceled
(let* ([x (make-parameter
0)] [lnull
] [add (lambda (a b) (set! l (append
l (list
(cons
a b)))))]) (let ([k (parameterize ([x 5]) (dynamic-wind
(lambda () (add 1 (x))) (lambda () (parameterize ([x 6]) (let ([k+e (let/cc k (cons
kvoid
))]) (add 2 (x)) ((cdr
k+e)) (car
k+e)))) (lambda () (add 3 (x)))))]) (parameterize ([x 7]) (let/cc esc (k (cons
void
esc))))) l) ; =>'((1 . 5) (2 . 6) (3 . 5) (1 . 5) (2 . 6) (3 . 5))
6.5 Prompts and Composable Continuations
For an introduction to composable continuations, see Sitaram and Felleisen, ``Control Delimiters and Their Hierarchies,'' Lisp and Symbolic Computation, 1990.
MzScheme's support for prompts and composable continuations most
closely resembles Dorai Sitaram's %
and fcontrol
operators (see ``Handling Control,'' Proc. Conference on
Programming Language Design and Implementation, 1993). Since
composable continuations capture and invoke
thunks, however, the dynamic-wind
fcontrol
operator is split into separate
capture and abort operations, giving programmers more flexibility with
respect to escapes. Composable continuations also capture continuation
marks (see section 6.6).
See also Chapter 13 in PLT MzLib: Libraries Manual for wrappers of MzScheme's primitives. The wrapper are generally simpler to use and have more standard names.
(call-with-continuation-prompt
thunk
[prompt-tag handler-proc-or-false
])
calls thunk
with the current continuation extended by a
prompt. The prompt is tagged by prompt-tag
, which must be a
result from either
(the
default) or default-continuation-prompt-tag
. The
make-continuation-prompt-tag
handler-proc-or-false
argument specifies a handler procedure;
the handler is called in tail position with repsect to
the call-with-continuation-prompt
call when the installed prompt is
the target of a abort-current-continuation
call with
prompt-tag
, and the remaining arguments of
abort-current-continuation
are supplied to the handler
procedure. If handler-proc-or-false
is #f
or not supplied,
the default handler accepts a single abort-thunk
argument and calls
(
; that is,
the default handler re-installs the prompt and continues with a given thunk.call-with-continuation-prompt
abort-thunk prompt-tag #f)
(abort-continuation-prompt
prompt-tag obj
···1)
resets the current continuation to that of the nearest prompt tagged
by prompt-tag
in the current continuation; if no such prompt exists,
the exn:fail:contract:continuation
exception is raised. The obj
s are delivered
as arguments to the target prompt's handler procedure.
(make-continuation-prompt-tag
[symbol
])
creates a prompt tag that is not
to the result of any
other value (including prior or future results from
equal?
). The optional make-continuation-prompt-tag
symbol
argument, if supplied, is used when printing the prompt tag.
(default-continuation-prompt-tag
)
returns a constant prompt tag for a which a prompt is installed at the start of
every thread's continuation; the handler for each thread's initial
prompt accepts any number of values and returns. The result of
default-continuation-prompt-tag
is the default tag for
more any procedure that accepts a prompt tag.
A continuation captured by (
is truncated at the nearest prompt tagged by
call-with-current-continuation
... promt-tag)prompt-tag
in the current continuation; if no such prompt
exists, the exn:fail:contract:continuation
exception is raised. The truncated
continuation includes only
thunks (see
section 6.4) installed since the prompt.dynamic-wind
When a continuation procedure is applied, it removes the portion of
the current continuation up to the nearest prompt tagged by
prompt-tag
(not including the prompt; if not such prompt it
exists, the exn:fail:contract:continuation
exception is raised), or up to the
nearest continuation frame (if any) shared by the current and captured
continuations -- whichever is first. While removing continuation frames,
dynamic-wind
post-thunk
s are executed. Finally, the
(unshared portion of the) captured continuation is appended to the remaining
continuation, applying dynamic-wind
pre-thunk
s.
(call-with-composable-continuation
proc
[prompt-tag
])
is similar to
, but applying
the resulting continuation procedure does not remove any portion of
the current continuation. Instead, application always extends the
current continuation with the captured continuation (without
installing any prompts other than those be captured in the
continuation). When call-with-current-continuation
is
called, if a continuation barrier appears in the continuation before
the closest prompt tagged by call-with-composable-continuation
prompt-tag
, the
exn:fail:contract:continuation
exception is raised.
(continuation-prompt-available?
prompt-tag
[cont
])
returns #t
if cont
includes a prompt tagged by prompt-tag
,
#f
otherwise. The cont
argument defaults to the
current continuation.
Examples:
6.6 Continuation Marks
To evaluate a sub-expression, MzScheme creates a continuation for the
sub-expression that extends the current continuation. For example, to
evaluate
in the expression
expr1
(beginexpr1
expr2
)
MzScheme extends the continuation of the begin
expression
with one continuation frame to create the continuation for
. In contrast, expr1
is in tail
position for the expr2
begin
expression, so its continuation is the
same as the continuation of the begin
expression.
A continuation mark is a keyed mark in a continuation frame. A program can install a mark in the first frame of its current continuation, and it can extract the marks from all of the frames in any continuation (up to the nearest prompt for a specified prompt tag).
Continuation marks support debuggers and other program-tracing facilities; in particular, continuation frames roughly correspond to stack frames in traditional languages. For example, when a procedure is called, MzScheme automatically installs a continuation mark with the procedure's name and source location; when an exception occurs, the marks can be extracted from the current continuation to produce a ``stack trace'' for the exception.15 A more sophisticated debugger can annotate a source program to store continuation marks that relate individual expressions to source locations.
The list of continuation marks for a key k
and a continuation
C
that extends C0
is defined as follows:
If
C
is an empty continuation, then the mark list is
.null
If
C
's first frame contains a markm
fork
, then the mark list forC
is(cons
, wherem
l0
)l0
is the mark list fork
inC0
.If
C
's first frame does not contain a mark keyed byk
, then the mark list forC
is the mark list forC0
.
The with-continuation-mark
form installs a mark on the
first frame of the current continuation:
(with-continuation-mark key-expr mark-expr body-expr)
The key-expr
, mark-expr
, and body-expr
expressions
are evaluated in order. After key-expr
is evaluated to obtain a
key and mark-expr
is evaluated to obtain a mark, the key is
mapped to the mark in the current continuation's initial frame. If
the frame already has a mark for the key, it is replaced. Finally,
the body-expr
is evaluated; the continuation for evaluating
body-expr
is the continuation of the
with-continuation-mark
expression (so the result of the
body-expr
is the result of the with-continuation-mark
expression, and body-expr
is in tail position for the
with-continuation-mark
expression).
The
procedure extracts the complete set of
continuation marks from a continuation (up to a prompt), and the
continuation-marks
procedure extracts mark values
for a particular key from a continuation mark set. The complete set
of continuation-mark procedures follows:
continuation-mark-set->list
(continuation-marks
cont
[prompt-tag
])
returns an opaque value containing the set of continuation marks for all keys in the continuationcont
up to the prompt tagged byprompt-tag
. Ifcont
is an escape continuation (see section 6.3), then the current continuation must extendcont
, or theexn:fail:contract
exception is raised. Ifcont
was not captured with respect toprompt-tag
and does not include a prompt forprompt-tag
, theexn:fail:contract
exception is raised. Theprompt-tag
argument defaults to(
default-continuation-prompt-tag
)(current-continuation-marks
[prompt-tag
])
returns an opaque value containing the set of continuation marks for all keys in the current continuation up toprompt-tag
. In other words, it produces the same value as(
. As usual,call-with-current-continuation
(lambda (k) (continuation-marks
k prompt-tag)) prompt-tag)prompt-tag
defaults to(
.default-continuation-prompt-tag
)(continuation-mark-set->list
mark-set key-v
[prompt-tag
])
returns a newly-created list containing the marks forkey-v
inmark-set
, which is a set of marks returned by
. The result list is truncated at the first point, if any, where continuation frames were originally separated by a prompt tagged withcurrent-continuation-marks
prompt-tag
. As usual,prompt-tag
defaults to(
.default-continuation-prompt-tag
)(continuation-mark-set->list*
mark-set key-list
[none-v prompt-tag
])
returns a newly-created list containing vectors of marks inmark-set
for the keys inkey-list
, up toprompt-tag
. The length of each vector in the result list is the same as the length ofkey-list
, and a value in a particular vector position is the value for the corresponding key inkey-list
. Values for multiple keys appear in a single vector only when the marks are for the same continuation frame inmark-set
. Ifnone-v
is supplied, it is used for vector elements to indicate the lack of a value; the default is#f
.(continuation-mark-set-first
optional-mark-set key-v
[prompt-tag
])
returns the first element of the list that would be returned by(
, orcontinuation-mark-set->list
(or optional-mark-set (current-continuation-marks
prompt-tag)) key-v prompt-tag)#f
if the result would be the empty list. Typically, this result can be computed more quickly usingcontinuation-mark-set-first
.(continuation-mark-set?
v
)
returns#t
ifv
is a mark set created by
orcontinuation-marks
,current-continuation-marks
#f
otherwise.(continuation-mark-set->context
mark-set
)
returns a list representing a ``stack trace'' formark-set
's continuation. The list contains pairs, where the
of each pair contains eithercar
#f
or a symbol for a procedure name, and the
of each pair contains eithercdr
#f
or asrcloc
value for the procedure's source location (see section 11.2.1.1); the
andcar
are never bothcdr
#f
.The stack-trace list is the result of
continuation-mark-set->list
withmark-set
and MzScheme's private key for procedure-call marks. A stack trace is extracted from an exception and displayed by the default error display handler (see section 6) for exceptions other thanexn:fail:user
(seeraise-user-error
in section 6.2).
Examples:
(define (extract-current-continuation-marks key) (continuation-mark-set->list
(current-continuation-marks
) key)) (with-continuation-mark 'key 'mark (extract-current-continuation-marks 'key)) ; =>'(mark)
(with-continuation-mark 'key1 'mark1 (with-continuation-mark 'key2 'mark2 (list
(extract-current-continuation-marks 'key1) (extract-current-continuation-marks 'key2)))) ; =>'((mark1) (mark2))
(with-continuation-mark 'key 'mark1 (with-continuation-mark 'key 'mark2 ; replaces the previous mark (extract-current-continuation-marks 'key)))) ; =>'(mark2)
(with-continuation-mark 'key 'mark1 (list
; continuation extended to evaluate the argument (with-continuation-mark 'key 'mark2 (extract-current-continuation-marks 'key)))) ; =>'((mark1 mark2))
(let loop ([n 1000]) (if (zero?
n) (extract-current-continuation-marks 'key) (with-continuation-mark 'key n (loop (sub1
n))))) ; =>'(1)
In the final example, the continuation mark is set 1000 times, but
extract-current-continuation-marks
returns only one mark
value. Because loop
is called tail-recursively, the
continuation of each call to loop
is always the continuation of
the entire expression. Therefore, the with-continuation-mark
expression replaces the existing mark each time rather than adding a
new one.
Whenever MzScheme creates an exception record, it fills the
field with the value of
continuation-marks
(current-continuation-marks)
, thus providing a snapshot of the
continuation marks at the time of the exception.
When a continuation procedure returned by
or
call-with-current-continuation
is invoked, it restores the
captured continuation, and also restores the marks in the
continuation's frames to the marks that were present when
call-with-composable-continuation
or
call-with-current-continuation
was invoked.call-with-composable-continuation
6.7 Breaks
A break is an asynchronous exception, usually triggered
through an external source controlled by the user, or through the
break-thread
procedure (see section 7.3). A break
exception can only occur in a thread while breaks are enabled. When a
break is detected and enabled, the exn:break
exception is raised in the thread
sometime afterward; if breaking is disabled when
is called, the break is suspended until
breaking is again enabled for the thread. While a thread has a
suspended break, additional breaks are ignored.break-thread
Breaks are enabled through the
parameter-like
procedure, and through the break-enabled
parameterize-break
form,
which is analogous to parameterize
(see
section 7.9). The
procedure does not
represent a parameter to be used with break-enabled
parameterize
, because
changing the break-enabled state of a thread requires an explicit
check for breaks, and this check is incompatible with the tail
evaluation of a parameterize
expression's body.
(break-enabled
[on?
])
-- gets or sets the break enabled state of the current thread. Ifon?
is not supplied, the result is#t
if break are currently enabled,#f
otherwise. Ifon?
is supplied as#f
, breaks are disabled, and ifon?
is a true value, breaks are enabled.(parameterize-break boolean-expr expr ···1)
evaluatesboolean-expr
to determine whether breaks are initially enabled in while evaluatingexpr
s in sequence. The result of theparameter-break
expression is the result of the lastexpr
.Like
parameterize
(see section 7.9), a fresh thread cell (see section 7.8) is allocated to hold the break-enabled state of the continuation, and calls tobreak-enabled
within the continuation access or modify the new cell.(current-break-parameterization
)
is analogous to(current-parameterization)
(see section 7.9); it returns a break-parameterization (effectively a thread cell) that holds the current continuation's break-enable state.(call-with-break-parameterization
break-param thunk
)
is analogous to(call-with-parameterization parameterization thunk)
(see section 7.9); it callsthunk
in a continuation whose break-enabled state is inbreak-param
. Thethunk
is not called in tail position with respect to thecall-with-break-parameterization
call.
Certain procedures, such as
,
enable breaks temporarily while performing a blocking action. If
breaks are enabled for a thread, and if a break is triggered for the
thread but not yet delivered as an semaphore-wait/enable-break
exn:break
exception, then
the break is guaranteed to be delivered before breaks can be disabled
in the thread. The timing of exn:break
exceptions is not
guaranteed in any other way.
Before calling a with-handlers
predicate or handler, an
exception handler, an error display handler, an error escape handler,
an error value conversion handler, or a pre-thunk
or
post-thunk
for a
(see
section 6.4), the call is dynamic-wind
parameterize-break
ed to
disable breaks. Furthermore, breaks are disabled during the
transitions among handlers related to exceptions, during the
transitions between pre-thunk
s and post-thunk
s for
, and during other transitions for a
continuation jump. For example, if breaks are disabled when a
continuation is invoked, and if breaks are also disabled in the
target continuation, then breaks will remain disabled until from the
time of the invocation until the target continuation executes unless
a relevant dynamic-wind
dynamic-wind
pre-thunk
or post-thunk
explicitly enables breaks.
If a break is triggered for a thread that is blocked on a nested
thread (see
), and if breaks are
enabled in the blocked thread, the break is implicitly handled by
transferring it to the nested thread.call-in-nested-thread
When breaks are enabled, they can occur at any point within execution, which makes certain implementation tasks subtle. For example, assuming breaks are enabled when the following code is executed,
(with-handlers ([exn:break?
(lambda (x) (void
))]) (semaphore-wait
s))
then it is not the case that a void result means the
semaphore was decremented or a break was received, exclusively. It is possible that both occur: the break may
occur after the semaphore is successfully decremented but before a
void result is returned by
. A break
exception will never damage a semaphore, or any other built-in
construct, but many built-in procedures (including
semaphore-wait
) contain internal sub-expressions that can be
interrupted by a break.semaphore-wait
In general, it is impossible using only
to
implement the guarantee that either the semaphore is decremented or
an exception is raised, but not both. MzScheme therefore supplies
semaphore-wait
(see section 7.4), which
does permit the implementation of such an exclusive guarantee:
semaphore-wait/enable-break
(parameterize ([break-enabled
#f]) (with-handlers ([exn:break?
(lambda (x) (void
))]) (semaphore-wait/enable-break
s)))
In the above expression, a break can occur at any point until breaks
are disabled, in which case a break exception is propagated to the
enclosing exception handler. Otherwise, the break can only occur
within
, which guarantees that if a
break exception is raised, the semaphore will not have been
decremented.semaphore-wait/enable-break
To allow similar implementation patterns over blocking port
operations, MzScheme provides
(see section 11.2.1),
read-bytes-avail!/enable-break
write-bytes-avail/enable-break
(see section 11.2.2), and
other procedures.
6.8 Error Escape Handler
Special control flow for exceptions is performed by an error
escape handler that is called by the default exception handler. An
error escape handler takes no arguments and must escape from the
expression that raised the exception. The error escape handler is
obtained or set using the error-escape-handler
parameter
(see section 7.9.1.7).
An error escape handler cannot invoke a full continuation that was created prior to the exception, but it can invoke an escape continuation (see section 6.3).
The error escape handler is normally called directly by an exception
handler, in a parameterization that sets the error display and escape
handlers to the default handlers, and parameterize-break
ed to
disable breaks. To escape from a run-time error, use
(see section 6.1) or raise
(see section 6.2)
instead.error
If an exception is raised while the error escape handler is executing, an error message is printed using a primitive error printer and a primitive error escape handler is invoked.
In the following example, the error escape handler is set so that
errors do not escape from a custom read
-eval
-print
loop:
(let ([orig (error-escape-handler
)]) (let/ecexit
(let retry-loop () (let/ec escape (error-escape-handler
(lambda () (escape #f))) (let loop () (let ([e
(my-read)]) (if (eof-object?e
) (exit
'done) (let ([v (my-evale
)]) (my-print v) (loop)))))) (retry-loop))) (error-escape-handler
orig))
See also
in section 14.1 for a simpler
implementation of this example.read-eval-print-loop
13 See http://www.cs.indiana.edu/scheme-repository/doc.proposals.exceptions.html
14 If the current error display handler is the default handler, then the error-display call is parameterized to install an emergency error display handler that attempts to print directly to a console and never fails.
15 Since stack-trace marks are applied dynamically,
they do not necessarily correspond to uses
of with-continuation-mark
on the source, and stack-trace marks
can be affected by optimization or just-in-time compilation of the
code. A stack traces is therefore useful as a debugging hint only.