mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-05-20 11:40:18 +02:00
Update documentation for with-exception-handler et al
* doc/ref/api-control.texi (Prompt Primitives): Reference the newer exception facilities. (Exceptions): Rewrite to use the new exception primitives. (Exception Terminology): Remove superfluous section. (Exception Objects): New section. (Raising and Handling Exceptions): New section. (Throw and Catch): New section, coalescing the previous catch, with-throw-handler, and throw sections. (Exceptions and C): New section, for miscellaneous procedures. (Handling Errors): Mention the transitional period regarding exception handling. * doc/ref/api-debug.texi (Catching Exceptions): Rewrite to use newer exception facilities. (Capturing Stacks): Remove, as it's not really recommendable any more. (Pre-Unwind Debugging): Rewrite to use the new primitives. (Standard Error Handling): Add note about transitional status. (Stack Overflow): Reference new exception section. * doc/ref/api-scheduling.texi (Mutexes and Condition Variables): Reference new exception section. * doc/ref/r6rs.texi (rnrs exceptions, rnrs conditions): Update to mention compatibility with SRFI-34/35 and to relate to core exceptions. * doc/ref/srfi-modules.texi (SRFI-34): Document.
This commit is contained in:
parent
f4ca107f7f
commit
44ee8c5559
5 changed files with 638 additions and 603 deletions
|
@ -18,7 +18,7 @@ flow of Scheme affects C code.
|
|||
* Prompts:: Composable, delimited continuations.
|
||||
* Continuations:: Non-composable continuations.
|
||||
* Multiple Values:: Returning and accepting multiple values.
|
||||
* Exceptions:: Throwing and catching exceptions.
|
||||
* Exceptions:: Raising and handling exceptions.
|
||||
* Error Reporting:: Procedures for signaling errors.
|
||||
* Dynamic Wind:: Dealing with non-local entrance/exit.
|
||||
* Fluids and Dynamic States:: Dynamic scope building blocks.
|
||||
|
@ -514,12 +514,13 @@ Unwind the dynamic and control context to the nearest prompt named @var{tag},
|
|||
also passing the given values.
|
||||
@end deffn
|
||||
|
||||
C programmers may recognize @code{call-with-prompt} and @code{abort-to-prompt}
|
||||
as a fancy kind of @code{setjmp} and @code{longjmp}, respectively. Prompts are
|
||||
indeed quite useful as non-local escape mechanisms. Guile's @code{catch} and
|
||||
@code{throw} are implemented in terms of prompts. Prompts are more convenient
|
||||
than @code{longjmp}, in that one has the opportunity to pass multiple values to
|
||||
the jump target.
|
||||
C programmers may recognize @code{call-with-prompt} and
|
||||
@code{abort-to-prompt} as a fancy kind of @code{setjmp} and
|
||||
@code{longjmp}, respectively. Prompts are indeed quite useful as
|
||||
non-local escape mechanisms. Guile's @code{with-exception-handler} and
|
||||
@code{raise-exception} are implemented in terms of prompts. Prompts are
|
||||
more convenient than @code{longjmp}, in that one has the opportunity to
|
||||
pass multiple values to the jump target.
|
||||
|
||||
Also unlike @code{longjmp}, the prompt handler is given the full state of the
|
||||
process that was aborted, as the first argument to the prompt's handler. That
|
||||
|
@ -586,7 +587,7 @@ important efficiency consideration to keep in mind.
|
|||
One example where this optimization matters is @dfn{escape
|
||||
continuations}. Escape continuations are delimited continuations whose
|
||||
only use is to make a non-local exit---i.e., to escape from the current
|
||||
continuation. A common use of escape continuations is when throwing an
|
||||
continuation. A common use of escape continuations is when handling an
|
||||
exception (@pxref{Exceptions}).
|
||||
|
||||
The constructs below are syntactic sugar atop prompts to simplify the
|
||||
|
@ -996,12 +997,20 @@ For example getting results from @code{partition} in SRFI-1
|
|||
@cindex error handling
|
||||
@cindex exception handling
|
||||
|
||||
A common requirement in applications is to want to jump
|
||||
@dfn{non-locally} from the depths of a computation back to, say, the
|
||||
application's main processing loop. Usually, the place that is the
|
||||
target of the jump is somewhere in the calling stack of procedures that
|
||||
called the procedure that wants to jump back. For example, typical
|
||||
logic for a key press driven application might look something like this:
|
||||
What happens when things go wrong? Guile's exception facility exists to
|
||||
help answer this question, allowing programs to describe the problem and
|
||||
to handle the situation in a flexible way.
|
||||
|
||||
When a program runs into a problem, such as division by zero, it will
|
||||
raise an exception. Sometimes exceptions get raised by Guile on a
|
||||
program's behalf. Sometimes a program will want to raise exceptions of
|
||||
its own. Raising an exception stops the current computation and instead
|
||||
invokes the current exception handler, passing it an exception object
|
||||
describing the unexpected situation.
|
||||
|
||||
Usually an exception handler will unwind the computation back to some
|
||||
kind of safe point. For example, typical logic for a key press driven
|
||||
application might look something like this:
|
||||
|
||||
@example
|
||||
main-loop:
|
||||
|
@ -1016,175 +1025,493 @@ find-file:
|
|||
find-specified-file
|
||||
|
||||
find-specified-file:
|
||||
check whether file exists; if not, jump back to main-loop
|
||||
check whether file exists; if not, raise an exception
|
||||
@dots{}
|
||||
@end example
|
||||
|
||||
The jump back to @code{main-loop} could be achieved by returning through
|
||||
the stack one procedure at a time, using the return value of each
|
||||
procedure to indicate the error condition, but Guile (like most modern
|
||||
programming languages) provides an additional mechanism called
|
||||
@dfn{exception handling} that can be used to implement such jumps much
|
||||
more conveniently.
|
||||
In this case, @code{main-loop} can install an exception handler that
|
||||
would cause any exception raised inside @code{dispatch-key} to print a
|
||||
warning and jump back to the main loop.
|
||||
|
||||
The following subsections go into more detail about exception objects,
|
||||
raising exceptions, and handling exceptions. It also presents a
|
||||
historical interface that was used in Guile's first 25 years and which
|
||||
won't be going away any time soon.
|
||||
|
||||
@menu
|
||||
* Exception Terminology:: Different ways to say the same thing.
|
||||
* Catch:: Setting up to catch exceptions.
|
||||
* Throw Handlers:: Handling exceptions before unwinding the stack.
|
||||
* Throw:: Throwing an exception.
|
||||
* Exception Implementation:: How Guile implements exceptions.
|
||||
* Exception Objects:: What went wrong?
|
||||
* Raising and Handling Exceptions:: What to do when something goes wrong.
|
||||
* Throw and Catch:: An older approach to exceptions.
|
||||
* Exceptions and C:: Specialized interfaces for C.
|
||||
@end menu
|
||||
|
||||
|
||||
@node Exception Terminology
|
||||
@subsubsection Exception Terminology
|
||||
@node Exception Objects
|
||||
@subsubsection Exception Objects
|
||||
|
||||
There are several variations on the terminology for dealing with
|
||||
non-local jumps. It is useful to be aware of them, and to realize
|
||||
that they all refer to the same basic mechanism.
|
||||
When Guile encounters an exceptional situation, it raises an exception,
|
||||
where the exception is an object that describes the exceptional
|
||||
situation. Exception objects are structured data, built on the record
|
||||
facility (@pxref{Records}).
|
||||
|
||||
@itemize @bullet
|
||||
@item
|
||||
Actually making a non-local jump may be called @dfn{raising an
|
||||
exception}, @dfn{raising a signal}, @dfn{throwing an exception} or
|
||||
@dfn{doing a long jump}. When the jump indicates an error condition,
|
||||
people may talk about @dfn{signalling}, @dfn{raising} or @dfn{throwing}
|
||||
@dfn{an error}.
|
||||
@deftp {Exception Type} &exception
|
||||
The base exception type. All exception objects are composed of
|
||||
instances of subtypes of @code{&exception}.
|
||||
@end deftp
|
||||
|
||||
@item
|
||||
Handling the jump at its target may be referred to as @dfn{catching} or
|
||||
@dfn{handling} the @dfn{exception}, @dfn{signal} or, where an error
|
||||
condition is involved, @dfn{error}.
|
||||
@end itemize
|
||||
@deffn {Scheme Procedure} exception-type? obj
|
||||
Return true if @var{obj} is an exception type.
|
||||
@end deffn
|
||||
|
||||
Where @dfn{signal} and @dfn{signalling} are used, special care is needed
|
||||
to avoid the risk of confusion with POSIX signals.
|
||||
Exception types exist in a hierarchy. New exception types can be
|
||||
defined using @code{make-exception-type}.
|
||||
|
||||
This manual prefers to speak of throwing and catching exceptions, since
|
||||
this terminology matches the corresponding Guile primitives.
|
||||
@deffn {Scheme Procedure} make-exception-type id parent field-names
|
||||
Return a new exception type named @var{id}, inheriting from
|
||||
@var{parent}, and with the fields whose names are listed in
|
||||
@var{field-names}. @var{field-names} must be a list of symbols and must
|
||||
not contain names already used by @var{parent} or one of its supertypes.
|
||||
@end deffn
|
||||
|
||||
The exception mechanism described in this section has connections with
|
||||
@dfn{delimited continuations} (@pxref{Prompts}). In particular,
|
||||
throwing an exception is akin to invoking an @dfn{escape continuation}
|
||||
(@pxref{Prompt Primitives, @code{call/ec}}).
|
||||
Exception type objects are record type objects, and as such, one can use
|
||||
@code{record-constructor} on an exception type to get its constructor.
|
||||
The constructor will take as many arguments as the exception has fields
|
||||
(including supertypes). @xref{Records}.
|
||||
|
||||
However, @code{record-predicate} and @code{record-accessor} aren't
|
||||
usually what you want to use as exception type predicates and field
|
||||
accessors. The reason is, instances of exception types can be composed
|
||||
into @dfn{compound exceptions}. Exception accessors should pick out the
|
||||
specific component of a compound exception, and then access the field on
|
||||
that specific component.
|
||||
|
||||
@deffn {Scheme Procedure} make-exception exceptions @dots{}
|
||||
Return an exception object composed of @var{exceptions}.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} exception? obj
|
||||
Return true if @var{obj} is an exception object.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} exception-predicate type
|
||||
Return a procedure that will return true if its argument is a simple
|
||||
exception that is an instance of @var{type}, or a compound exception
|
||||
composed of such an instance.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} exception-accessor rtd proc
|
||||
Return a procedure that will tail-call @var{proc} on an instance of the
|
||||
exception type @var{rtd}, or on the component of a compound exception
|
||||
that is an instance of @var{rtd}.
|
||||
@end deffn
|
||||
|
||||
Compound exceptions are useful to separately express the different
|
||||
aspects of a situation. For example, compound exceptions allow a
|
||||
programmer to say that ``this situation is a programming error, and also
|
||||
here's a useful message to show to the user, and here are some relevant
|
||||
objects that can give more information about the error''. This error
|
||||
could be composed of instances of the @code{&programming-error},
|
||||
@code{&message}, and @code{&irritants} exception types.
|
||||
|
||||
The subtyping relationship in exceptions is useful to let
|
||||
different-but-similar situations to be treated the same; for example
|
||||
there are many varieties of programming errors (for example,
|
||||
divide-by-zero or type mismatches), but perhaps there are common ways
|
||||
that the user would like to handle them all, and that common way might
|
||||
be different than how one might handle an error originating outside the
|
||||
program (for example, a file-not-found error).
|
||||
|
||||
The standard exception hierarchy in Guile takes its cues from R6RS,
|
||||
though the names of some of the types are different. @xref{rnrs
|
||||
exceptions}, for more details.
|
||||
|
||||
To have access to Guile's exception type hierarchy, import the
|
||||
@code{(ice-9 exceptions)} module:
|
||||
|
||||
@example
|
||||
(use-modules (ice-9 exceptions))
|
||||
@end example
|
||||
|
||||
The following diagram gives an overview of the standard exception type
|
||||
hierarchy.
|
||||
|
||||
@example
|
||||
&exception
|
||||
|- &warning
|
||||
|- &message
|
||||
|- &irritants
|
||||
|- &origin
|
||||
\- &error
|
||||
|- &external-error
|
||||
\- &programming-error
|
||||
|- &assertion-failure
|
||||
|- &non-continuable
|
||||
|- &implementation-restriction
|
||||
|- &lexical
|
||||
|- &syntax
|
||||
\- &undefined-variable
|
||||
@end example
|
||||
|
||||
@deftp {Exception Type} &warning
|
||||
An exception type denoting warnings. These are usually raised using
|
||||
@code{#:continuable? #t}; see the @code{raise-exception} documentation
|
||||
for more.
|
||||
@end deftp
|
||||
@deffn {Scheme Procedure} make-warning
|
||||
@deffnx {Scheme Procedure} warning? obj
|
||||
Constructor and predicate for @code{&warning} exception objects.
|
||||
@end deffn
|
||||
|
||||
@deftp {Exception Type} &message message
|
||||
An exception type that provides a message to display to the user.
|
||||
Usually used as a component of a compound exception.
|
||||
@end deftp
|
||||
@deffn {Scheme Procedure} make-exception-with-message message
|
||||
@deffnx {Scheme Procedure} exception-with-message? obj
|
||||
@deffnx {Scheme Procedure} exception-message exn
|
||||
Constructor, predicate, and accessor for @code{&message} exception
|
||||
objects.
|
||||
@end deffn
|
||||
|
||||
@deftp {Exception Type} &irritants irritants
|
||||
An exception type that provides a list of objects that were unexpected
|
||||
in some way. Usually used as a component of a compound exception.
|
||||
@end deftp
|
||||
@deffn {Scheme Procedure} make-exception-with-irritants irritants
|
||||
@deffnx {Scheme Procedure} exception-with-irritants? obj
|
||||
@deffnx {Scheme Procedure} exception-irritants exn
|
||||
Constructor, predicate, and accessor for @code{&irritants} exception
|
||||
objects.
|
||||
@end deffn
|
||||
|
||||
@deftp {Exception Type} &origin origin
|
||||
An exception type that indicates the origin of an exception, typically
|
||||
expressed as a procedure name, as a symbol. Usually used as a component
|
||||
of a compound exception.
|
||||
@end deftp
|
||||
@deffn {Scheme Procedure} make-exception-with-origin origin
|
||||
@deffnx {Scheme Procedure} exception-with-origin? obj
|
||||
@deffnx {Scheme Procedure} exception-origin exn
|
||||
Constructor, predicate, and accessor for @code{&origin} exception
|
||||
objects.
|
||||
@end deffn
|
||||
|
||||
@deftp {Exception Type} &error
|
||||
An exception type denoting errors: situations that are not just
|
||||
exceptional, but wrong.
|
||||
@end deftp
|
||||
@deffn {Scheme Procedure} make-error
|
||||
@deffnx {Scheme Procedure} error? obj
|
||||
Constructor and predicate for @code{&error} exception objects.
|
||||
@end deffn
|
||||
|
||||
@deftp {Exception Type} &external-error
|
||||
An exception type denoting errors that proceed from the interaction of
|
||||
the program with the world, for example a ``file not found'' error.
|
||||
@end deftp
|
||||
@deffn {Scheme Procedure} make-external-error
|
||||
@deffnx {Scheme Procedure} external-error? obj
|
||||
Constructor and predicate for @code{&external-error} exception objects.
|
||||
@end deffn
|
||||
|
||||
@deftp {Exception Type} &programming-error
|
||||
An exception type denoting errors that proceed from inside a program:
|
||||
type mismatches and so on.
|
||||
@end deftp
|
||||
@deffn {Scheme Procedure} make-programming-error
|
||||
@deffnx {Scheme Procedure} programming-error? obj
|
||||
Constructor and predicate for @code{&programming-error} exception
|
||||
objects.
|
||||
@end deffn
|
||||
|
||||
@deftp {Exception Type} &non-continuable
|
||||
An exception type denoting errors that proceed from inside a program:
|
||||
type mismatches and so on.
|
||||
@end deftp
|
||||
@deffn {Scheme Procedure} make-non-continuable-error
|
||||
@deffnx {Scheme Procedure} non-continuable-error? obj
|
||||
Constructor and predicate for @code{&non-continuable} exception objects.
|
||||
@end deffn
|
||||
|
||||
@deftp {Exception Type} &lexical
|
||||
An exception type denoting lexical errors, for example unbalanced
|
||||
parentheses.
|
||||
@end deftp
|
||||
@deffn {Scheme Procedure} make-lexical-error
|
||||
@deffnx {Scheme Procedure} lexical-error? obj
|
||||
Constructor and predicate for @code{&lexical} exception objects.
|
||||
@end deffn
|
||||
|
||||
@deftp {Exception Type} &syntax form subform
|
||||
An exception type denoting syntax errors, for example a @code{cond}
|
||||
expression with invalid syntax. The @var{form} field indicates the form
|
||||
containing the error, and @var{subform} indicates the unexpected
|
||||
subcomponent, or @code{#f} if unavailable.
|
||||
@end deftp
|
||||
@deffn {Scheme Procedure} make-syntax-error form subform
|
||||
@deffnx {Scheme Procedure} syntax-error? obj
|
||||
@deffnx {Scheme Procedure} syntax-error-form exn
|
||||
@deffnx {Scheme Procedure} syntax-error-subform exn
|
||||
Constructor, predicate, and accessors for @code{&syntax} exception
|
||||
objects.
|
||||
@end deffn
|
||||
|
||||
@deftp {Exception Type} &undefined-variable
|
||||
An exception type denoting undefined variables.
|
||||
@end deftp
|
||||
@deffn {Scheme Procedure} make-undefine-variable-error
|
||||
@deffnx {Scheme Procedure} undefined-variable-error? obj
|
||||
Constructor and predicate for @code{&undefined-variable} exception
|
||||
objects.
|
||||
@end deffn
|
||||
|
||||
Incidentally, the @code{(ice-9 exceptions)} module also includes a
|
||||
@code{define-exception-type} macro that can be used to conveniently add
|
||||
new exception types to the hierarchy.
|
||||
|
||||
@deffn {Syntax} define-exception-type name parent @
|
||||
constructor predicate @
|
||||
(field accessor) @dots{}
|
||||
Define @var{name} to be a new exception type, inheriting from
|
||||
@var{parent}. Define @var{constructor} and @var{predicate} to be the
|
||||
exception constructor and predicate, respectively, and define an
|
||||
@var{accessor} for each @var{field}.
|
||||
@end deffn
|
||||
|
||||
|
||||
@node Catch
|
||||
@subsubsection Catching Exceptions
|
||||
@node Raising and Handling Exceptions
|
||||
@subsubsection Raising and Handling Exceptions
|
||||
|
||||
@code{catch} is used to set up a target for a possible non-local jump.
|
||||
The arguments of a @code{catch} expression are a @dfn{key}, which
|
||||
restricts the set of exceptions to which this @code{catch} applies, a
|
||||
thunk that specifies the code to execute and one or two @dfn{handler}
|
||||
procedures that say what to do if an exception is thrown while executing
|
||||
the code. If the execution thunk executes @dfn{normally}, which means
|
||||
without throwing any exceptions, the handler procedures are not called
|
||||
at all.
|
||||
An exception object describes an exceptional situation. To bring that
|
||||
description to the attention of the user or to handle the situation
|
||||
programmatically, the first step is to @dfn{raise} the exception.
|
||||
|
||||
When an exception is thrown using the @code{throw} function, the first
|
||||
argument of the @code{throw} is a symbol that indicates the type of the
|
||||
exception. For example, Guile throws an exception using the symbol
|
||||
@code{numerical-overflow} to indicate numerical overflow errors such as
|
||||
division by zero:
|
||||
@deffn {Scheme Procedure} raise-exception obj [#:continuable=#f]
|
||||
Raise an exception by invoking the current exception handler on
|
||||
@var{obj}. The handler is called with a continuation whose dynamic
|
||||
environment is that of the call to @code{raise}, except that the current
|
||||
exception handler is the one that was in place when the handler being
|
||||
called was installed.
|
||||
|
||||
@lisp
|
||||
(/ 1 0)
|
||||
@result{}
|
||||
ABORT: (numerical-overflow)
|
||||
@end lisp
|
||||
If @var{continuable?} is true, the handler is invoked in tail position
|
||||
relative to the @code{raise-exception} call. Otherwise if the handler
|
||||
returns, a non-continuable exception of type @code{&non-continuable} is
|
||||
raised in the same dynamic environment as the handler.
|
||||
@end deffn
|
||||
|
||||
The @var{key} argument in a @code{catch} expression corresponds to this
|
||||
symbol. @var{key} may be a specific symbol, such as
|
||||
@code{numerical-overflow}, in which case the @code{catch} applies
|
||||
specifically to exceptions of that type; or it may be @code{#t}, which
|
||||
means that the @code{catch} applies to all exceptions, irrespective of
|
||||
their type.
|
||||
As the above description notes, Guile has a notion of a @dfn{current
|
||||
exception handler}. At the REPL, this exception handler may enter a
|
||||
recursive debugger; in a standalone program, it may simply print a
|
||||
representation of the error and exit.
|
||||
|
||||
The second argument of a @code{catch} expression should be a thunk
|
||||
(i.e.@: a procedure that accepts no arguments) that specifies the normal
|
||||
case code. The @code{catch} is active for the execution of this thunk,
|
||||
including any code called directly or indirectly by the thunk's body.
|
||||
Evaluation of the @code{catch} expression activates the catch and then
|
||||
calls this thunk.
|
||||
To establish an exception handler within the dynamic extent of a call,
|
||||
use @code{with-exception-handler}.
|
||||
|
||||
The third argument of a @code{catch} expression is a handler procedure.
|
||||
If an exception is thrown, this procedure is called with exactly the
|
||||
arguments specified by the @code{throw}. Therefore, the handler
|
||||
procedure must be designed to accept a number of arguments that
|
||||
corresponds to the number of arguments in all @code{throw} expressions
|
||||
that can be caught by this @code{catch}.
|
||||
@deffn {Scheme Procedure} with-exception-handler handler thunk @
|
||||
[#:unwind?=#f] [#:unwind-for-type=#t]
|
||||
Establish @var{handler}, a procedure of one argument, as the current
|
||||
exception handler during the dynamic extent of invoking @var{thunk}.
|
||||
|
||||
The fourth, optional argument of a @code{catch} expression is another
|
||||
handler procedure, called the @dfn{pre-unwind} handler. It differs from
|
||||
the third argument in that if an exception is thrown, it is called,
|
||||
@emph{before} the third argument handler, in exactly the dynamic context
|
||||
of the @code{throw} expression that threw the exception. This means
|
||||
that it is useful for capturing or displaying the stack at the point of
|
||||
the @code{throw}, or for examining other aspects of the dynamic context,
|
||||
such as fluid values, before the context is unwound back to that of the
|
||||
prevailing @code{catch}.
|
||||
If @code{raise-exception} is called during the dynamic extent of
|
||||
invoking @var{thunk}, @var{handler} will be invoked on the argument of
|
||||
@code{raise-exception}.
|
||||
@end deffn
|
||||
|
||||
There are two kinds of exception handlers: unwinding and non-unwinding.
|
||||
|
||||
By default, exception handlers are non-unwinding. Unless
|
||||
@code{with-exception-handler} was invoked with @code{#:unwind? #t},
|
||||
exception handlers are invoked within the continuation of the error,
|
||||
without unwinding the stack. The dynamic environment of the handler
|
||||
call will be that of the @code{raise-exception} call, with the
|
||||
difference that the current exception handler will be ``unwound'' to the
|
||||
\"outer\" handler (the one that was in place when the corresponding
|
||||
@code{with-exception-handler} was called).
|
||||
|
||||
However, it's often the case that one would like to handle an exception
|
||||
by unwinding the computation to an earlier state and running the error
|
||||
handler there. After all, unless the @code{raise-exception} call is
|
||||
continuable, the exception handler needs to abort the continuation. To
|
||||
support this use case, if @code{with-exception-handler} was invoked with
|
||||
@code{#:unwind? #t} is true, @code{raise-exception} will first unwind
|
||||
the stack by invoking an @dfn{escape continuation} (@pxref{Prompt
|
||||
Primitives, @code{call/ec}}), and then invoke the handler with the
|
||||
continuation of the @code{with-exception-handler} call.
|
||||
|
||||
Finally, one more wrinkle: for unwinding exception handlers, it can be
|
||||
useful to Guile if it can determine whether an exception handler would
|
||||
indeed handle a particular exception or not. This is especially the
|
||||
case for exceptions raised in resource-exhaustion scenarios like
|
||||
@code{stack-overflow} or @code{out-of-memory}, where you want to
|
||||
immediately shrink resource use before recovering. @xref{Stack
|
||||
Overflow}. For this purpose, the @code{#:unwind-for-type} keyword
|
||||
argument allows users to specify the kind of exception handled by an
|
||||
exception handler; if @code{#t}, all exceptions will be handled; if an
|
||||
exception type object, only exceptions of that type will be handled;
|
||||
otherwise if a symbol, only that exceptions with the given
|
||||
@code{exception-kind} will be handled.
|
||||
|
||||
|
||||
@node Throw and Catch
|
||||
@subsubsection Throw and Catch
|
||||
|
||||
Guile only adopted @code{with-exception-handler} and
|
||||
@code{raise-exception} as its primary exception-handling facility in
|
||||
2019. Before then, exception handling was fundamentally based on three
|
||||
other primitives with a somewhat more complex interface: @code{catch},
|
||||
@code{with-throw-handler}, and @code{throw}.
|
||||
|
||||
@deffn {Scheme Procedure} catch key thunk handler [pre-unwind-handler]
|
||||
@deffnx {C Function} scm_catch_with_pre_unwind_handler (key, thunk, handler, pre_unwind_handler)
|
||||
@deffnx {C Function} scm_catch (key, thunk, handler)
|
||||
Invoke @var{thunk} in the dynamic context of @var{handler} for
|
||||
exceptions matching @var{key}. If thunk throws to the symbol
|
||||
@var{key}, then @var{handler} is invoked this way:
|
||||
@lisp
|
||||
(handler key args ...)
|
||||
@end lisp
|
||||
Establish an exception handler during the dynamic extent of the call to
|
||||
@var{thunk}. @var{key} is either @code{#t}, indicating that all
|
||||
exceptions should be handled, or a symbol, restricting the exceptions
|
||||
handled to those having the @var{key} as their @code{exception-kind}.
|
||||
|
||||
@var{key} is a symbol or @code{#t}.
|
||||
|
||||
@var{thunk} takes no arguments. If @var{thunk} returns
|
||||
normally, that is the return value of @code{catch}.
|
||||
|
||||
Handler is invoked outside the scope of its own @code{catch}.
|
||||
If @var{handler} again throws to the same key, a new handler
|
||||
from further up the call chain is invoked.
|
||||
|
||||
If the key is @code{#t}, then a throw to @emph{any} symbol will
|
||||
match this call to @code{catch}.
|
||||
|
||||
If a @var{pre-unwind-handler} is given and @var{thunk} throws
|
||||
an exception that matches @var{key}, Guile calls the
|
||||
@var{pre-unwind-handler} before unwinding the dynamic state and
|
||||
invoking the main @var{handler}. @var{pre-unwind-handler} should
|
||||
be a procedure with the same signature as @var{handler}, that
|
||||
is @code{(lambda (key . args))}. It is typically used to save
|
||||
the stack at the point where the exception occurred, but can also
|
||||
query other parts of the dynamic state at that point, such as
|
||||
fluid values.
|
||||
|
||||
A @var{pre-unwind-handler} can exit either normally or non-locally.
|
||||
If it exits normally, Guile unwinds the stack and dynamic context
|
||||
and then calls the normal (third argument) handler. If it exits
|
||||
non-locally, that exit determines the continuation.
|
||||
If @var{thunk} executes normally, meaning without throwing any
|
||||
exceptions, the handler procedures are not called at all and the result
|
||||
of the @code{thunk} call is the result of the @code{catch}. Otherwise
|
||||
if an exception is thrown that matches @var{key}, @var{handler} is
|
||||
called with the continuation of the @code{catch} call.
|
||||
@end deffn
|
||||
|
||||
If a handler procedure needs to match a variety of @code{throw}
|
||||
expressions with varying numbers of arguments, you should write it like
|
||||
this:
|
||||
Given the discussion from the previous section, it is most precise and
|
||||
concise to specify what @code{catch} does by expressing it in terms of
|
||||
@code{with-exception-handler}. Calling @code{catch} with the three
|
||||
arguments is the same as:
|
||||
|
||||
@example
|
||||
(define (catch key thunk handler)
|
||||
(with-exception-handler
|
||||
(lambda (exn)
|
||||
(apply handler (exception-kind exn) (exception-args exn)))
|
||||
thunk
|
||||
#:unwind? #t
|
||||
#:unwind-for-type key))
|
||||
@end example
|
||||
|
||||
By invoking @code{with-exception-handler} with @code{#:unwind? #t},
|
||||
@code{catch} sets up an escape continuation that will be invoked in an
|
||||
exceptional situation before the handler is called.
|
||||
|
||||
If @code{catch} is called with four arguments, then the use of
|
||||
@var{thunk} should be replaced with:
|
||||
|
||||
@example
|
||||
(lambda ()
|
||||
(with-throw-handler key thunk pre-unwind-handler))
|
||||
@end example
|
||||
|
||||
As can be seen above, if a pre-unwind-handler is passed to @code{catch},
|
||||
it's like calling @code{with-throw-handler} inside the body thunk.
|
||||
|
||||
@code{with-throw-handler} is the second of the older primitives, and is
|
||||
used to be able to intercept an exception that is being thrown before
|
||||
the stack is unwound. This could be to clean up some related state, to
|
||||
print a backtrace, or to pass information about the exception to a
|
||||
debugger, for example.
|
||||
|
||||
@deffn {Scheme Procedure} with-throw-handler key thunk handler
|
||||
@deffnx {C Function} scm_with_throw_handler (key, thunk, handler)
|
||||
Add @var{handler} to the dynamic context as a throw handler
|
||||
for key @var{key}, then invoke @var{thunk}.
|
||||
@end deffn
|
||||
|
||||
It's not possible to exactly express @code{with-throw-handler} in terms
|
||||
of @code{with-exception-handler}, but we can get close.
|
||||
|
||||
@example
|
||||
(define (with-throw-handler key thunk handler)
|
||||
(with-exception-handler
|
||||
(lambda (exn)
|
||||
(when (or (eq? key #t) (eq? key (exception-kind exn)))
|
||||
(apply handler (exception-kind exn) (exception-args exn)))
|
||||
(raise-exception exn))
|
||||
thunk))
|
||||
@end example
|
||||
|
||||
As you can see, unlike in the case of @code{catch}, the handler for
|
||||
@code{with-throw-handler} is invoked within the continuation of
|
||||
@code{raise-exception}, before unwinding the stack. If the throw
|
||||
handler returns normally, the exception will be re-raised, to be handled
|
||||
by the next exception handler.
|
||||
|
||||
The special wrinkle of @code{with-throw-handler} that can't be shown
|
||||
above is that if invoking the handler causes a @code{raise-exception}
|
||||
instead of completing normally, the exception is thrown in the
|
||||
@emph{original} dynamic environment of the @code{raise-exception}. Any
|
||||
inner exception handler will get another shot at handling the exception.
|
||||
Here is an example to illustrate this behavior:
|
||||
|
||||
@lisp
|
||||
(lambda (key . args)
|
||||
@dots{})
|
||||
(catch 'a
|
||||
(lambda ()
|
||||
(with-throw-handler 'b
|
||||
(lambda ()
|
||||
(catch 'a
|
||||
(lambda ()
|
||||
(throw 'b))
|
||||
inner-handler))
|
||||
(lambda (key . args)
|
||||
(throw 'a))))
|
||||
outer-handler)
|
||||
@end lisp
|
||||
|
||||
@noindent
|
||||
The @var{key} argument is guaranteed always to be present, because a
|
||||
@code{throw} without a @var{key} is not valid. The number and
|
||||
interpretation of the @var{args} varies from one type of exception to
|
||||
another, but should be specified by the documentation for each exception
|
||||
type.
|
||||
This code will call @code{inner-handler} and then continue with the
|
||||
continuation of the inner @code{catch}.
|
||||
|
||||
Note that, once the normal (post-unwind) handler procedure is invoked,
|
||||
the catch that led to the handler procedure being called is no longer
|
||||
active. Therefore, if the handler procedure itself throws an exception,
|
||||
that exception can only be caught by another active catch higher up the
|
||||
call stack, if there is one.
|
||||
Finally, we get to @code{throw}, which is the older equivalent to
|
||||
@code{raise-exception}.
|
||||
|
||||
@deffn {Scheme Procedure} throw key arg @dots{}
|
||||
@deffnx {C Function} scm_throw (key, args)
|
||||
Raise an exception with kind @var{key} and arguments @var{args}.
|
||||
@var{key} is a symbol, denoting the ``kind'' of the exception.
|
||||
@end deffn
|
||||
|
||||
Again, we can specify what @code{throw} does by expressing it in terms
|
||||
of @code{raise-exception}.
|
||||
|
||||
@example
|
||||
(define (throw key . args)
|
||||
(raise-exception (make-exception-from-throw key args)))
|
||||
@end example
|
||||
|
||||
At this point, we should mention the primitive that manage the
|
||||
relationship between structured exception objects @code{throw}.
|
||||
|
||||
@deffn {Scheme Procedure} make-exception-from-throw key args
|
||||
Create an exception object for the given @var{key} and @var{args} passed
|
||||
to @code{throw}. This may be a specific type of exception, for example
|
||||
@code{&programming-error}; Guile maintains a set of custom transformers
|
||||
for the various @var{key} values that have been used historically.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} exception-kind exn
|
||||
If @var{exn} is an exception created via
|
||||
@code{make-exception-from-throw}, return the corresponding @var{key} for
|
||||
the exception. Otherwise, unless @var{exn} is an exception of a type
|
||||
with a known mapping to @code{throw}, return the symbol
|
||||
@code{%exception}.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} exception-args exn
|
||||
If @var{exn} is an exception created via
|
||||
@code{make-exception-from-throw}, return the corresponding @var{args}
|
||||
for the exception. Otherwise, unless @var{exn} is an exception of a
|
||||
type with a known mapping to @code{throw}, return @code{(list @var{exn})}.
|
||||
@end deffn
|
||||
|
||||
|
||||
@node Exceptions and C
|
||||
@subsubsection Exceptions and C
|
||||
|
||||
There are some specific versions of Guile's original @code{catch} and
|
||||
@code{with-throw-handler} exception-handling primitives that are still
|
||||
widely used in C code.
|
||||
|
||||
@sp 1
|
||||
@deftypefn {C Function} SCM scm_c_catch (SCM tag, scm_t_catch_body body, void *body_data, scm_t_catch_handler handler, void *handler_data, scm_t_catch_handler pre_unwind_handler, void *pre_unwind_handler_data)
|
||||
@deftypefnx {C Function} SCM scm_internal_catch (SCM tag, scm_t_catch_body body, void *body_data, scm_t_catch_handler handler, void *handler_data)
|
||||
The above @code{scm_catch_with_pre_unwind_handler} and @code{scm_catch}
|
||||
|
@ -1223,207 +1550,14 @@ value must be held on the stack. Another way is to use
|
|||
Management}).
|
||||
@end deftypefn
|
||||
|
||||
|
||||
@node Throw Handlers
|
||||
@subsubsection Throw Handlers
|
||||
|
||||
It's sometimes useful to be able to intercept an exception that is being
|
||||
thrown before the stack is unwound. This could be to clean up some
|
||||
related state, to print a backtrace, or to pass information about the
|
||||
exception to a debugger, for example. The @code{with-throw-handler}
|
||||
procedure provides a way to do this.
|
||||
|
||||
@deffn {Scheme Procedure} with-throw-handler key thunk handler
|
||||
@deffnx {C Function} scm_with_throw_handler (key, thunk, handler)
|
||||
Add @var{handler} to the dynamic context as a throw handler
|
||||
for key @var{key}, then invoke @var{thunk}.
|
||||
|
||||
This behaves exactly like @code{catch}, except that it does not unwind
|
||||
the stack before invoking @var{handler}. If the @var{handler} procedure
|
||||
returns normally, Guile rethrows the same exception again to the next
|
||||
innermost catch or throw handler. @var{handler} may exit nonlocally, of
|
||||
course, via an explicit throw or via invoking a continuation.
|
||||
@end deffn
|
||||
|
||||
Typically @var{handler} is used to display a backtrace of the stack at
|
||||
the point where the corresponding @code{throw} occurred, or to save off
|
||||
this information for possible display later.
|
||||
|
||||
Not unwinding the stack means that throwing an exception that is handled
|
||||
via a throw handler is equivalent to calling the throw handler handler
|
||||
inline instead of each @code{throw}, and then omitting the surrounding
|
||||
@code{with-throw-handler}. In other words,
|
||||
|
||||
@lisp
|
||||
(with-throw-handler 'key
|
||||
(lambda () @dots{} (throw 'key args @dots{}) @dots{})
|
||||
handler)
|
||||
@end lisp
|
||||
|
||||
@noindent
|
||||
is mostly equivalent to
|
||||
|
||||
@lisp
|
||||
((lambda () @dots{} (handler 'key args @dots{}) @dots{}))
|
||||
@end lisp
|
||||
|
||||
In particular, the dynamic context when @var{handler} is invoked is that
|
||||
of the site where @code{throw} is called. The examples are not quite
|
||||
equivalent, because the body of a @code{with-throw-handler} is not in
|
||||
tail position with respect to the @code{with-throw-handler}, and if
|
||||
@var{handler} exits normally, Guile arranges to rethrow the error, but
|
||||
hopefully the intention is clear. (For an introduction to what is meant
|
||||
by dynamic context, @xref{Dynamic Wind}.)
|
||||
|
||||
@deftypefn {C Function} SCM scm_c_with_throw_handler (SCM tag, scm_t_catch_body body, void *body_data, scm_t_catch_handler handler, void *handler_data, int lazy_catch_p)
|
||||
The above @code{scm_with_throw_handler} takes Scheme procedures as body
|
||||
(thunk) and handler arguments. @code{scm_c_with_throw_handler} is an
|
||||
equivalent taking C functions. See @code{scm_c_catch} (@pxref{Catch})
|
||||
for a description of the parameters, the behaviour however of course
|
||||
follows @code{with-throw-handler}.
|
||||
equivalent taking C functions. See @code{scm_c_catch}
|
||||
(@pxref{Exceptions and C}) for a description of the parameters, the
|
||||
behaviour however of course follows @code{with-throw-handler}.
|
||||
@end deftypefn
|
||||
|
||||
If @var{thunk} throws an exception, Guile handles that exception by
|
||||
invoking the innermost @code{catch} or throw handler whose key matches
|
||||
that of the exception. When the innermost thing is a throw handler,
|
||||
Guile calls the specified handler procedure using @code{(apply
|
||||
@var{handler} key args)}. The handler procedure may either return
|
||||
normally or exit non-locally. If it returns normally, Guile passes the
|
||||
exception on to the next innermost @code{catch} or throw handler. If it
|
||||
exits non-locally, that exit determines the continuation.
|
||||
|
||||
The behaviour of a throw handler is very similar to that of a
|
||||
@code{catch} expression's optional pre-unwind handler. In particular, a
|
||||
throw handler's handler procedure is invoked in the exact dynamic
|
||||
context of the @code{throw} expression, just as a pre-unwind handler is.
|
||||
@code{with-throw-handler} may be seen as a half-@code{catch}: it does
|
||||
everything that a @code{catch} would do until the point where
|
||||
@code{catch} would start unwinding the stack and dynamic context, but
|
||||
then it rethrows to the next innermost @code{catch} or throw handler
|
||||
instead.
|
||||
|
||||
Note also that since the dynamic context is not unwound, if a
|
||||
@code{with-throw-handler} handler throws to a key that does not match
|
||||
the @code{with-throw-handler} expression's @var{key}, the new throw may
|
||||
be handled by a @code{catch} or throw handler that is @emph{closer} to
|
||||
the throw than the first @code{with-throw-handler}.
|
||||
|
||||
Here is an example to illustrate this behavior:
|
||||
|
||||
@lisp
|
||||
(catch 'a
|
||||
(lambda ()
|
||||
(with-throw-handler 'b
|
||||
(lambda ()
|
||||
(catch 'a
|
||||
(lambda ()
|
||||
(throw 'b))
|
||||
inner-handler))
|
||||
(lambda (key . args)
|
||||
(throw 'a))))
|
||||
outer-handler)
|
||||
@end lisp
|
||||
|
||||
@noindent
|
||||
This code will call @code{inner-handler} and then continue with the
|
||||
continuation of the inner @code{catch}.
|
||||
|
||||
|
||||
@node Throw
|
||||
@subsubsection Throwing Exceptions
|
||||
|
||||
The @code{throw} primitive is used to throw an exception. One argument,
|
||||
the @var{key}, is mandatory, and must be a symbol; it indicates the type
|
||||
of exception that is being thrown. Following the @var{key},
|
||||
@code{throw} accepts any number of additional arguments, whose meaning
|
||||
depends on the exception type. The documentation for each possible type
|
||||
of exception should specify the additional arguments that are expected
|
||||
for that kind of exception.
|
||||
|
||||
@deffn {Scheme Procedure} throw key arg @dots{}
|
||||
@deffnx {C Function} scm_throw (key, args)
|
||||
Invoke the catch form matching @var{key}, passing @var{arg} @dots{} to
|
||||
the @var{handler}.
|
||||
|
||||
@var{key} is a symbol. It will match catches of the same symbol or of
|
||||
@code{#t}.
|
||||
|
||||
If there is no handler at all, Guile prints an error and then exits.
|
||||
@end deffn
|
||||
|
||||
When an exception is thrown, it will be caught by the innermost
|
||||
@code{catch} or throw handler that applies to the type of the thrown
|
||||
exception; in other words, whose @var{key} is either @code{#t} or the
|
||||
same symbol as that used in the @code{throw} expression. Once Guile has
|
||||
identified the appropriate @code{catch} or throw handler, it handles the
|
||||
exception by applying the relevant handler procedure(s) to the arguments
|
||||
of the @code{throw}.
|
||||
|
||||
If there is no appropriate @code{catch} or throw handler for a thrown
|
||||
exception, Guile prints an error to the current error port indicating an
|
||||
uncaught exception, and then exits. In practice, it is quite difficult
|
||||
to observe this behaviour, because Guile when used interactively
|
||||
installs a top level @code{catch} handler that will catch all exceptions
|
||||
and print an appropriate error message @emph{without} exiting. For
|
||||
example, this is what happens if you try to throw an unhandled exception
|
||||
in the standard Guile REPL; note that Guile's command loop continues
|
||||
after the error message:
|
||||
|
||||
@lisp
|
||||
guile> (throw 'badex)
|
||||
<unnamed port>:3:1: In procedure gsubr-apply @dots{}
|
||||
<unnamed port>:3:1: unhandled-exception: badex
|
||||
ABORT: (misc-error)
|
||||
guile>
|
||||
@end lisp
|
||||
|
||||
The default uncaught exception behaviour can be observed by evaluating a
|
||||
@code{throw} expression from the shell command line:
|
||||
|
||||
@example
|
||||
$ guile -c "(begin (throw 'badex) (display \"here\\n\"))"
|
||||
guile: uncaught throw to badex: ()
|
||||
$
|
||||
@end example
|
||||
|
||||
@noindent
|
||||
That Guile exits immediately following the uncaught exception
|
||||
is shown by the absence of any output from the @code{display}
|
||||
expression, because Guile never gets to the point of evaluating that
|
||||
expression.
|
||||
|
||||
|
||||
@node Exception Implementation
|
||||
@subsubsection How Guile Implements Exceptions
|
||||
|
||||
It is traditional in Scheme to implement exception systems using
|
||||
@code{call-with-current-continuation}. Continuations
|
||||
(@pxref{Continuations}) are such a powerful concept that any other
|
||||
control mechanism --- including @code{catch} and @code{throw} --- can be
|
||||
implemented in terms of them.
|
||||
|
||||
Guile does not implement @code{catch} and @code{throw} like this,
|
||||
though. Why not? Because Guile is specifically designed to be easy to
|
||||
integrate with applications written in C. In a mixed Scheme/C
|
||||
environment, the concept of @dfn{continuation} must logically include
|
||||
``what happens next'' in the C parts of the application as well as the
|
||||
Scheme parts, and it turns out that the only reasonable way of
|
||||
implementing continuations like this is to save and restore the complete
|
||||
C stack.
|
||||
|
||||
So Guile's implementation of @code{call-with-current-continuation} is a
|
||||
stack copying one. This allows it to interact well with ordinary C
|
||||
code, but means that creating and calling a continuation is slowed down
|
||||
by the time that it takes to copy the C stack.
|
||||
|
||||
The more targeted mechanism provided by @code{catch} and @code{throw}
|
||||
does not need to save and restore the C stack because the @code{throw}
|
||||
always jumps to a location higher up the stack of the code that executes
|
||||
the @code{throw}. Therefore Guile implements the @code{catch} and
|
||||
@code{throw} primitives independently of
|
||||
@code{call-with-current-continuation}, in a way that takes advantage of
|
||||
this @emph{upwards only} nature of exceptions.
|
||||
|
||||
|
||||
@node Error Reporting
|
||||
@subsection Procedures for Signaling Errors
|
||||
|
@ -2023,8 +2157,13 @@ Guile's parameters conform to SRFI-39 (@pxref{SRFI-39}).
|
|||
@node Handling Errors
|
||||
@subsection How to Handle Errors
|
||||
|
||||
Error handling is based on @code{catch} and @code{throw}. Errors are
|
||||
always thrown with a @var{key} and four arguments:
|
||||
Guile is currently in a transition from its historical @code{catch} and
|
||||
@code{throw} error handling and signaling operators to the new
|
||||
structured exception facility; @xref{Exceptions}. However in the
|
||||
meantime, here is some documentation on errors and the older
|
||||
@code{catch} and @code{throw} interface.
|
||||
|
||||
Errors are always thrown with a @var{key} and four arguments:
|
||||
|
||||
@itemize @bullet
|
||||
@item
|
||||
|
|
|
@ -356,8 +356,8 @@ library, or from Guile itself.
|
|||
|
||||
@menu
|
||||
* Catching Exceptions:: Handling errors after the stack is unwound.
|
||||
* Capturing Stacks:: Capturing the stack at the time of error.
|
||||
* Pre-Unwind Debugging:: Debugging before the exception is thrown.
|
||||
* Standard Error Handling:: Call-with-error-handling.
|
||||
* Stack Overflow:: Detecting and handling runaway recursion.
|
||||
* Debug Options:: A historical interface to debugging.
|
||||
@end menu
|
||||
|
@ -370,230 +370,64 @@ possible when a Scheme program hits an error. The most immediate
|
|||
information about an error is the kind of error that it is -- such as
|
||||
``division by zero'' -- and any parameters that the code which signalled
|
||||
the error chose explicitly to provide. This information originates with
|
||||
the @code{error} or @code{throw} call (or their C code equivalents, if
|
||||
the error is detected by C code) that signals the error, and is passed
|
||||
automatically to the handler procedure of the innermost applicable
|
||||
@code{catch} or @code{with-throw-handler} expression.
|
||||
the @code{error} or @code{raise-exception} call (or their C code
|
||||
equivalents, if the error is detected by C code) that signals the error,
|
||||
and is passed automatically to the handler procedure of the innermost
|
||||
applicable exception handler.
|
||||
|
||||
Therefore, to catch errors that occur within a chunk of Scheme code, and
|
||||
to intercept basic information about those errors, you need to execute
|
||||
that code inside the dynamic context of a @code{catch} or
|
||||
@code{with-throw-handler} expression, or the equivalent in C. In Scheme,
|
||||
this means you need something like this:
|
||||
|
||||
@lisp
|
||||
(catch #t
|
||||
(lambda ()
|
||||
;; Execute the code in which
|
||||
;; you want to catch errors here.
|
||||
...)
|
||||
(lambda (key . parameters)
|
||||
;; Put the code which you want
|
||||
;; to handle an error here.
|
||||
...))
|
||||
@end lisp
|
||||
|
||||
@noindent
|
||||
The @code{catch} here can also be @code{with-throw-handler}; see
|
||||
@ref{Throw Handlers} for information on the when you might want to use
|
||||
@code{with-throw-handler} instead of @code{catch}.
|
||||
that code inside the dynamic context of a @code{with-exception-handler},
|
||||
or the equivalent in C.
|
||||
|
||||
For example, to print out a message and return #f when an error occurs,
|
||||
you might use:
|
||||
|
||||
@smalllisp
|
||||
(define (catch-all thunk)
|
||||
(catch #t
|
||||
thunk
|
||||
(lambda (key . parameters)
|
||||
(with-exception-handler
|
||||
(lambda (exn)
|
||||
(format (current-error-port)
|
||||
"Uncaught throw to '~a: ~a\n" key parameters)
|
||||
#f)))
|
||||
"Uncaught exception: ~s\n" exn)
|
||||
#f)
|
||||
thunk
|
||||
#:unwind? #t))
|
||||
|
||||
(catch-all
|
||||
(lambda () (error "Not a vegetable: tomato")))
|
||||
@print{} Uncaught throw to 'misc-error: (#f ~A (Not a vegetable: tomato) #f)
|
||||
@print{} Uncaught exception: #<&exception-with-kind-and-args ...>
|
||||
@result{} #f
|
||||
@end smalllisp
|
||||
|
||||
The @code{#t} means that the catch is applicable to all kinds of error.
|
||||
If you want to restrict your catch to just one kind of error, you can
|
||||
put the symbol for that kind of error instead of @code{#t}. The
|
||||
equivalent to this in C would be something like this:
|
||||
@xref{Exceptions}, for full details.
|
||||
|
||||
@lisp
|
||||
SCM my_body_proc (void *body_data)
|
||||
@{
|
||||
/* Execute the code in which
|
||||
you want to catch errors here. */
|
||||
...
|
||||
@}
|
||||
|
||||
SCM my_handler_proc (void *handler_data,
|
||||
SCM key,
|
||||
SCM parameters)
|
||||
@{
|
||||
/* Put the code which you want
|
||||
to handle an error here. */
|
||||
...
|
||||
@}
|
||||
|
||||
@{
|
||||
...
|
||||
scm_c_catch (SCM_BOOL_T,
|
||||
my_body_proc, body_data,
|
||||
my_handler_proc, handler_data,
|
||||
NULL, NULL);
|
||||
...
|
||||
@}
|
||||
@end lisp
|
||||
|
||||
@noindent
|
||||
Again, as with the Scheme version, @code{scm_c_catch} could be replaced
|
||||
by @code{scm_c_with_throw_handler}, and @code{SCM_BOOL_T} could instead
|
||||
be the symbol for a particular kind of error.
|
||||
|
||||
@node Capturing Stacks
|
||||
@subsubsection Capturing the full error stack
|
||||
|
||||
The other interesting information about an error is the full Scheme
|
||||
stack at the point where the error occurred; in other words what
|
||||
innermost expression was being evaluated, what was the expression that
|
||||
called that one, and so on. If you want to write your code so that it
|
||||
captures and can display this information as well, there are a couple
|
||||
important things to understand.
|
||||
|
||||
Firstly, the stack at the point of the error needs to be explicitly
|
||||
captured by a @code{make-stack} call (or the C equivalent
|
||||
@code{scm_make_stack}). The Guile library does not do this
|
||||
``automatically'' for you, so you will need to write code with a
|
||||
@code{make-stack} or @code{scm_make_stack} call yourself. (We emphasise
|
||||
this point because some people are misled by the fact that the Guile
|
||||
interactive REPL code @emph{does} capture and display the stack
|
||||
automatically. But the Guile interactive REPL is itself a Scheme
|
||||
program@footnote{In effect, it is the default program which is run when
|
||||
no commands or script file are specified on the Guile command line.}
|
||||
running on top of the Guile library, and which uses @code{catch} and
|
||||
@code{make-stack} in the way we are about to describe to capture the
|
||||
stack when an error occurs.)
|
||||
|
||||
And secondly, in order to capture the stack effectively at the point
|
||||
where the error occurred, the @code{make-stack} call must be made before
|
||||
Guile unwinds the stack back to the location of the prevailing catch
|
||||
expression. This means that the @code{make-stack} call must be made
|
||||
within the handler of a @code{with-throw-handler} expression, or the
|
||||
optional "pre-unwind" handler of a @code{catch}. (For the full story of
|
||||
how these alternatives differ from each other, see @ref{Exceptions}. The
|
||||
main difference is that @code{catch} terminates the error, whereas
|
||||
@code{with-throw-handler} only intercepts it temporarily and then allow
|
||||
it to continue propagating up to the next innermost handler.)
|
||||
|
||||
So, here are some examples of how to do all this in Scheme and in C.
|
||||
For the purpose of these examples we assume that the captured stack
|
||||
should be stored in a variable, so that it can be displayed or
|
||||
arbitrarily processed later on. In Scheme:
|
||||
|
||||
@lisp
|
||||
(let ((captured-stack #f))
|
||||
(catch #t
|
||||
(lambda ()
|
||||
;; Execute the code in which
|
||||
;; you want to catch errors here.
|
||||
...)
|
||||
(lambda (key . parameters)
|
||||
;; Put the code which you want
|
||||
;; to handle an error after the
|
||||
;; stack has been unwound here.
|
||||
...)
|
||||
(lambda (key . parameters)
|
||||
;; Capture the stack here:
|
||||
(set! captured-stack (make-stack #t))))
|
||||
...
|
||||
(if captured-stack
|
||||
(begin
|
||||
;; Display or process the captured stack.
|
||||
...))
|
||||
...)
|
||||
@end lisp
|
||||
|
||||
@noindent
|
||||
And in C:
|
||||
|
||||
@lisp
|
||||
SCM my_body_proc (void *body_data)
|
||||
@{
|
||||
/* Execute the code in which
|
||||
you want to catch errors here. */
|
||||
...
|
||||
@}
|
||||
|
||||
SCM my_handler_proc (void *handler_data,
|
||||
SCM key,
|
||||
SCM parameters)
|
||||
@{
|
||||
/* Put the code which you want
|
||||
to handle an error after the
|
||||
stack has been unwound here. */
|
||||
...
|
||||
@}
|
||||
|
||||
SCM my_preunwind_proc (void *handler_data,
|
||||
SCM key,
|
||||
SCM parameters)
|
||||
@{
|
||||
/* Capture the stack here: */
|
||||
*(SCM *)handler_data = scm_make_stack (SCM_BOOL_T, SCM_EOL);
|
||||
@}
|
||||
|
||||
@{
|
||||
SCM captured_stack = SCM_BOOL_F;
|
||||
...
|
||||
scm_c_catch (SCM_BOOL_T,
|
||||
my_body_proc, body_data,
|
||||
my_handler_proc, handler_data,
|
||||
my_preunwind_proc, &captured_stack);
|
||||
...
|
||||
if (captured_stack != SCM_BOOL_F)
|
||||
@{
|
||||
/* Display or process the captured stack. */
|
||||
...
|
||||
@}
|
||||
...
|
||||
@}
|
||||
@end lisp
|
||||
|
||||
Once you have a captured stack, you can interrogate and display its
|
||||
details in any way that you want, using the @code{stack-@dots{}} and
|
||||
@code{frame-@dots{}} API described in @ref{Stacks} and
|
||||
@ref{Frames}.
|
||||
|
||||
If you want to print out a backtrace in the same format that the Guile
|
||||
REPL does, you can use the @code{display-backtrace} procedure to do so.
|
||||
You can also use @code{display-application} to display an individual
|
||||
frame in the Guile REPL format.
|
||||
|
||||
@node Pre-Unwind Debugging
|
||||
@subsubsection Pre-Unwind Debugging
|
||||
|
||||
Instead of saving a stack away and waiting for the @code{catch} to
|
||||
return, you can handle errors directly, from within the pre-unwind
|
||||
handler.
|
||||
Sometimes when something goes wrong, what you want is not just a
|
||||
representation of the exceptional situation, but the context that
|
||||
brought about that situation. The example in the previous section
|
||||
passed @code{#:unwind #t} to @code{with-exception-handler}, indicating
|
||||
that @code{raise-exception} should unwind the stack before invoking the
|
||||
exception handler. However if you don't take this approach and instead
|
||||
let the exception handler be invoked in the context of the
|
||||
@code{raise-exception}, you can print a backtrace, launch a recursive
|
||||
debugger, or take other ``pre-unwind'' actions.
|
||||
|
||||
For example, to show a backtrace when an error is thrown, you might want
|
||||
to use a procedure like this:
|
||||
The most basic idea would be to simply print a backtrace:
|
||||
|
||||
@lisp
|
||||
(define (with-backtrace thunk)
|
||||
(with-throw-handler #t
|
||||
thunk
|
||||
(lambda args (backtrace))))
|
||||
(with-backtrace (lambda () (error "Not a vegetable: tomato")))
|
||||
@end lisp
|
||||
@example
|
||||
(define (call-with-backtrace thunk)
|
||||
(with-exception-handler
|
||||
(lambda (exn)
|
||||
(backtrace)
|
||||
(raise-exception exn))
|
||||
thunk))
|
||||
@end example
|
||||
|
||||
Since we used @code{with-throw-handler} here, we didn't actually catch
|
||||
the error. @xref{Throw Handlers}, for more information. However, we did
|
||||
print out a context at the time of the error, using the built-in
|
||||
procedure, @code{backtrace}.
|
||||
Here we use the built-in @code{backtrace} procedure to print the
|
||||
backtrace.
|
||||
|
||||
@deffn {Scheme Procedure} backtrace [highlights]
|
||||
@deffnx {C Function} scm_backtrace_with_highlights (highlights)
|
||||
|
@ -603,6 +437,47 @@ Display a backtrace of the current stack to the current output port. If
|
|||
will be highlighted wherever they appear in the backtrace.
|
||||
@end deffn
|
||||
|
||||
By re-raising the exception, @code{call-with-backtrace} doesn't actually
|
||||
handle the error. We could define a version that instead aborts the
|
||||
computation:
|
||||
|
||||
@example
|
||||
(use-modules (ice-9 control))
|
||||
(define (call-with-backtrace thunk)
|
||||
(let/ec cancel
|
||||
(with-exception-handler
|
||||
(lambda (exn)
|
||||
(backtrace)
|
||||
(cancel #f))
|
||||
thunk)))
|
||||
@end example
|
||||
|
||||
In this second example, we use an escape continuation to abort the
|
||||
computation after printing the backtrace, returning @code{#f} instead.
|
||||
|
||||
It could be that you want to only print a limited backtrace. In that
|
||||
case, use @code{start-stack}:
|
||||
|
||||
@example
|
||||
(use-modules (ice-9 control))
|
||||
(define (call-with-backtrace thunk)
|
||||
(let/ec cancel
|
||||
(start-stack 'stack-with-backtrace
|
||||
(with-exception-handler
|
||||
(lambda (exn)
|
||||
(backtrace)
|
||||
(cancel #f))
|
||||
thunk))))
|
||||
@end example
|
||||
|
||||
There are also more powerful, programmatic ways to walk the stack using
|
||||
@code{make-stack} and friends; see the API described in @ref{Stacks} and
|
||||
@ref{Frames}.
|
||||
|
||||
|
||||
@node Standard Error Handling
|
||||
@subsubsection call-with-error-handling
|
||||
|
||||
The Guile REPL code (in @file{system/repl/repl.scm} and related files)
|
||||
uses a @code{catch} with a pre-unwind handler to capture the stack when
|
||||
an error occurs in an expression that was typed into the REPL, and debug
|
||||
|
@ -622,6 +497,12 @@ These procedures are available for use by user programs, in the
|
|||
[#:trap-handler trap-handler='debug]
|
||||
Call a thunk in a context in which errors are handled.
|
||||
|
||||
Note that this function was written when @code{throw}/@code{catch} were
|
||||
the fundamental exception handling primitives in Guile, and so exposes
|
||||
some aspects of that interface (notably in the form of the procedural
|
||||
handlers). Guile will probably replace this function with a
|
||||
@code{call-with-standard-exception-handling} in the future.
|
||||
|
||||
There are five keyword arguments:
|
||||
|
||||
@table @var
|
||||
|
@ -743,11 +624,11 @@ on @var{l}, not @code{(cdr @var{l})}. Running this program would cause
|
|||
Guile to use up all memory in your system, and eventually Guile would
|
||||
fail to grow the stack. At that point you have a problem: Guile needs
|
||||
to raise an exception to unwind the stack and return memory to the
|
||||
system, but the user might have throw handlers in place (@pxref{Throw
|
||||
Handlers}) that want to run before the stack is unwound, and we don't
|
||||
have any stack in which to run them.
|
||||
system, but the user might have exception handlers in place
|
||||
(@pxref{Raising and Handling Exceptions}) that want to run before the
|
||||
stack is unwound, and we don't have any stack in which to run them.
|
||||
|
||||
Therefore in this case, Guile throws an unwind-only exception that does
|
||||
Therefore in this case, Guile raises an unwind-only exception that does
|
||||
not run pre-unwind handlers. Because this is such an odd case, Guile
|
||||
prints out a message on the console, in case the user was expecting to
|
||||
be able to get a backtrace from any pre-unwind handler.
|
||||
|
|
|
@ -473,8 +473,8 @@ interrupts. What happens if you hold a mutex, but somehow you cause an
|
|||
exception to be thrown? There is no one right answer. You might want
|
||||
to keep the mutex locked to prevent any other code from ever entering
|
||||
that critical section again. Or, your critical section might be fine if
|
||||
you unlock the mutex ``on the way out'', via a catch handler or
|
||||
@code{dynamic-wind}. @xref{Catch}, and @xref{Dynamic Wind}.
|
||||
you unlock the mutex ``on the way out'', via an exception handler or
|
||||
@code{dynamic-wind}. @xref{Exceptions}, and @xref{Dynamic Wind}.
|
||||
|
||||
But if you arrange to unlock the mutex when leaving a dynamic extent via
|
||||
@code{dynamic-wind}, what to do if control re-enters that dynamic extent
|
||||
|
|
|
@ -1139,38 +1139,16 @@ descriptor @var{rtd} (and not any of its sub- or supertypes) is mutable.
|
|||
@subsubsection rnrs exceptions
|
||||
|
||||
The @code{(rnrs exceptions (6))} library provides functionality related
|
||||
to signaling and handling exceptional situations. This functionality is
|
||||
similar to the exception handling systems provided by Guile's core
|
||||
library @xref{Exceptions}, and by the SRFI-18 and SRFI-34
|
||||
modules---@xref{SRFI-18 Exceptions}, and @ref{SRFI-34},
|
||||
respectively---but there are some key differences in concepts and
|
||||
behavior.
|
||||
|
||||
A raised exception may be @dfn{continuable} or @dfn{non-continuable}.
|
||||
When an exception is raised non-continuably, another exception, with the
|
||||
condition type @code{&non-continuable}, will be raised when the
|
||||
exception handler returns locally. Raising an exception continuably
|
||||
captures the current continuation and invokes it after a local return
|
||||
from the exception handler.
|
||||
|
||||
Like SRFI-18 and SRFI-34, R6RS exceptions are implemented on top of
|
||||
Guile's native @code{throw} and @code{catch} forms, and use custom
|
||||
``throw keys'' to identify their exception types. As a consequence,
|
||||
Guile's @code{catch} form can handle exceptions thrown by these APIs,
|
||||
but the reverse is not true: Handlers registered by the
|
||||
@code{with-exception-handler} procedure described below will only be
|
||||
called on exceptions thrown by the corresponding @code{raise} procedure.
|
||||
to signaling and handling exceptional situations. This functionality
|
||||
re-exports Guile's core exception-handling primitives.
|
||||
@xref{Exceptions}, for a full discussion. @xref{SRFI-34}, for a similar
|
||||
pre-R6RS facility. In Guile, SRFI-34, SRFI-35, and R6RS exception
|
||||
handling are all built on the same core facilities, and so are
|
||||
interoperable.
|
||||
|
||||
@deffn {Scheme Procedure} with-exception-handler handler thunk
|
||||
Installs @var{handler}, which must be a procedure taking one argument,
|
||||
as the current exception handler during the invocation of @var{thunk}, a
|
||||
procedure taking zero arguments. The handler in place at the time
|
||||
@code{with-exception-handler} is called is made current again once
|
||||
either @var{thunk} returns or @var{handler} is invoked after an
|
||||
exception is thrown from within @var{thunk}.
|
||||
|
||||
This procedure is similar to the @code{with-throw-handler} procedure
|
||||
provided by Guile's code library; (@pxref{Throw Handlers}).
|
||||
@xref{Raising and Handling Exceptions}, for more information on
|
||||
@code{with-exception-handler}.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Syntax} guard (variable clause1 clause2 ...) body
|
||||
|
@ -1194,17 +1172,18 @@ evaluates to @code{baz}.
|
|||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} raise obj
|
||||
Raises a non-continuable exception by invoking the currently-installed
|
||||
exception handler on @var{obj}. If the handler returns, a
|
||||
@code{&non-continuable} exception will be raised in the dynamic context
|
||||
in which the handler was installed.
|
||||
Equivalent to core Guile @code{(raise-exception @var{obj})}.
|
||||
@xref{Raising and Handling Exceptions}. p(Unfortunately, @code{raise}
|
||||
is already bound to a different function in core Guile.
|
||||
@xref{Signals}.)
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} raise-continuable obj
|
||||
Raises a continuable exception by invoking currently-installed exception
|
||||
handler on @var{obj}.
|
||||
Equivalent to core Guile @code{(raise-exception @var{obj} #:continuable?
|
||||
#t)}. @xref{Raising and Handling Exceptions}.
|
||||
@end deffn
|
||||
|
||||
|
||||
@node rnrs conditions
|
||||
@subsubsection rnrs conditions
|
||||
|
||||
|
@ -1232,17 +1211,26 @@ component simple condition of the appropriate type; the field accessors
|
|||
return the requisite fields from the first component simple condition
|
||||
found to be of the appropriate type.
|
||||
|
||||
Guile's R6RS layer uses core exception types from the @code{(ice-9
|
||||
exceptions)} module as the basis for its R6RS condition system. Guile
|
||||
prefers to use the term ``exception object'' and ``exception type''
|
||||
rather than ``condition'' or ``condition type'', but that's just a
|
||||
naming difference. Guile also has different names for the types in the
|
||||
condition hierarchy. @xref{Exception Objects}, for full details.
|
||||
|
||||
This library is quite similar to the SRFI-35 conditions module
|
||||
(@pxref{SRFI-35}). Among other minor differences, the
|
||||
@code{(rnrs conditions)} library features slightly different semantics
|
||||
around condition field accessors, and comes with a larger number of
|
||||
pre-defined condition types. The two APIs are not currently compatible,
|
||||
however; the @code{condition?} predicate from one API will return
|
||||
@code{#f} when applied to a condition object created in the other.
|
||||
(@pxref{SRFI-35}). Among other minor differences, the @code{(rnrs
|
||||
conditions)} library features slightly different semantics around
|
||||
condition field accessors, and comes with a larger number of pre-defined
|
||||
condition types. The two APIs are compatible; the @code{condition?}
|
||||
predicate from one API will return @code{#t} when applied to a condition
|
||||
object created in the other. of the condition types are the same,
|
||||
also.
|
||||
|
||||
@deffn {Condition Type} &condition
|
||||
@deffnx {Scheme Procedure} condition? obj
|
||||
The base record type for conditions.
|
||||
The base record type for conditions. Known as @code{&exception} in core
|
||||
Guile.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} condition condition1 ...
|
||||
|
@ -1294,27 +1282,29 @@ A base type for representing non-fatal conditions during execution.
|
|||
@deffnx {Scheme Procedure} make-serious-condition
|
||||
@deffnx {Scheme Procedure} serious-condition? obj
|
||||
A base type for conditions representing errors serious enough that
|
||||
cannot be ignored.
|
||||
cannot be ignored. Known as @code{&error} in core Guile.
|
||||
@end deffn
|
||||
|
||||
@deffn {Condition Type} &error
|
||||
@deffnx {Scheme Procedure} make-error
|
||||
@deffnx {Scheme Procedure} error? obj
|
||||
A base type for conditions representing errors.
|
||||
A base type for conditions representing errors. Known as
|
||||
@code{&external-error} in core Guile.
|
||||
@end deffn
|
||||
|
||||
@deffn {Condition Type} &violation
|
||||
@deffnx {Scheme Procedure} make-violation
|
||||
@deffnx {Scheme Procedure} violation?
|
||||
A subtype of @code{&serious} that can be used to represent violations
|
||||
of a language or library standard.
|
||||
A subtype of @code{&serious} that can be used to represent violations of
|
||||
a language or library standard. Known as @code{&programming-error} in
|
||||
core Guile.
|
||||
@end deffn
|
||||
|
||||
@deffn {Condition Type} &assertion
|
||||
@deffnx {Scheme Procedure} make-assertion-violation
|
||||
@deffnx {Scheme Procedure} assertion-violation? obj
|
||||
A subtype of @code{&violation} that indicates an invalid call to a
|
||||
procedure.
|
||||
procedure. Known as @code{&assertion-failure} in core Guile.
|
||||
@end deffn
|
||||
|
||||
@deffn {Condition Type} &irritants
|
||||
|
@ -1368,7 +1358,7 @@ indicate the syntactic form responsible for the condition.
|
|||
@deffnx {Scheme Procedure} make-undefined-violation
|
||||
@deffnx {Scheme Procedure} undefined-violation? obj
|
||||
A subtype of @code{&violation} that indicates a reference to an unbound
|
||||
identifier.
|
||||
identifier. Known as @code{&undefined-variable} in core Guile.
|
||||
@end deffn
|
||||
|
||||
@node R6RS I/O Conditions
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
@c -*-texinfo-*-
|
||||
@c This is part of the GNU Guile Reference Manual.
|
||||
@c Copyright (C) 1996, 1997, 2000-2004, 2006, 2007-2014, 2017, 2018
|
||||
@c Copyright (C) 1996, 1997, 2000-2004, 2006, 2007-2014, 2017, 2018, 2019
|
||||
@c Free Software Foundation, Inc.
|
||||
@c See the file guile.texi for copying conditions.
|
||||
|
||||
|
@ -3320,7 +3320,26 @@ handling mechanisms} as an alternative to its own built-in mechanisms
|
|||
(use-modules (srfi srfi-34))
|
||||
@end lisp
|
||||
|
||||
@c FIXME: Document it.
|
||||
@xref{Raising and Handling Exceptions}, for more on
|
||||
@code{with-exception-handler} and @code{raise} (known as
|
||||
@code{raise-exception} in core Guile).
|
||||
|
||||
SRFI-34's @code{guard} form is syntactic sugar over
|
||||
@code{with-exception-handler}:
|
||||
|
||||
@deffn {Syntax} guard (var clause @dots{}) body @dots{}
|
||||
Evaluate @var{body} with an exception handler that binds the raised
|
||||
object to @var{var} and within the scope of that binding evaluates
|
||||
@var{clause}@dots{} as if they were the clauses of a cond expression.
|
||||
That implicit cond expression is evaluated with the continuation and
|
||||
dynamic environment of the guard expression.
|
||||
|
||||
If every @var{clause}'s test evaluates to false and there is no
|
||||
@code{else} clause, then @code{raise} is re-invoked on the raised object
|
||||
within the dynamic environment of the original call to @code{raise}
|
||||
except that the current exception handler is that of the @code{guard}
|
||||
expression.
|
||||
@end deffn
|
||||
|
||||
|
||||
@node SRFI-35
|
||||
|
@ -3330,7 +3349,7 @@ handling mechanisms} as an alternative to its own built-in mechanisms
|
|||
@cindex conditions
|
||||
@cindex exceptions
|
||||
|
||||
@uref{http://srfi.schemers.org/srfi-35/srfi-35.html, SRFI-35} implements
|
||||
@uref{http://srfi.schemers.org/srfi-35/srfi-35.html, SRFI-35} defines
|
||||
@dfn{conditions}, a data structure akin to records designed to convey
|
||||
information about exceptional conditions between parts of a program. It
|
||||
is normally used in conjunction with SRFI-34's @code{raise}:
|
||||
|
@ -3498,6 +3517,12 @@ the user.
|
|||
Return true if @var{c} is of type @code{&error} or one of its subtypes.
|
||||
@end deffn
|
||||
|
||||
As an implementation note, condition objects in Guile are the same as
|
||||
``exception objects''. @xref{Exception Objects}. The
|
||||
@code{&condition}, @code{&serious}, and @code{&error} condition types
|
||||
are known in core Guile as @code{&exception}, @code{&error}, and
|
||||
@code{&external-error}, respectively.
|
||||
|
||||
@node SRFI-37
|
||||
@subsection SRFI-37 - args-fold
|
||||
@cindex SRFI-37
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue