1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-05-29 16:30:19 +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:
Andy Wingo 2019-11-13 22:26:31 +01:00
parent f4ca107f7f
commit 44ee8c5559
5 changed files with 638 additions and 603 deletions

View file

@ -18,7 +18,7 @@ flow of Scheme affects C code.
* Prompts:: Composable, delimited continuations. * Prompts:: Composable, delimited continuations.
* Continuations:: Non-composable continuations. * Continuations:: Non-composable continuations.
* Multiple Values:: Returning and accepting multiple values. * Multiple Values:: Returning and accepting multiple values.
* Exceptions:: Throwing and catching exceptions. * Exceptions:: Raising and handling exceptions.
* Error Reporting:: Procedures for signaling errors. * Error Reporting:: Procedures for signaling errors.
* Dynamic Wind:: Dealing with non-local entrance/exit. * Dynamic Wind:: Dealing with non-local entrance/exit.
* Fluids and Dynamic States:: Dynamic scope building blocks. * 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. also passing the given values.
@end deffn @end deffn
C programmers may recognize @code{call-with-prompt} and @code{abort-to-prompt} C programmers may recognize @code{call-with-prompt} and
as a fancy kind of @code{setjmp} and @code{longjmp}, respectively. Prompts are @code{abort-to-prompt} as a fancy kind of @code{setjmp} and
indeed quite useful as non-local escape mechanisms. Guile's @code{catch} and @code{longjmp}, respectively. Prompts are indeed quite useful as
@code{throw} are implemented in terms of prompts. Prompts are more convenient non-local escape mechanisms. Guile's @code{with-exception-handler} and
than @code{longjmp}, in that one has the opportunity to pass multiple values to @code{raise-exception} are implemented in terms of prompts. Prompts are
the jump target. 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 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 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 One example where this optimization matters is @dfn{escape
continuations}. Escape continuations are delimited continuations whose continuations}. Escape continuations are delimited continuations whose
only use is to make a non-local exit---i.e., to escape from the current 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}). exception (@pxref{Exceptions}).
The constructs below are syntactic sugar atop prompts to simplify the 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 error handling
@cindex exception handling @cindex exception handling
A common requirement in applications is to want to jump What happens when things go wrong? Guile's exception facility exists to
@dfn{non-locally} from the depths of a computation back to, say, the help answer this question, allowing programs to describe the problem and
application's main processing loop. Usually, the place that is the to handle the situation in a flexible way.
target of the jump is somewhere in the calling stack of procedures that
called the procedure that wants to jump back. For example, typical When a program runs into a problem, such as division by zero, it will
logic for a key press driven application might look something like this: 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 @example
main-loop: main-loop:
@ -1016,175 +1025,493 @@ find-file:
find-specified-file find-specified-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{} @dots{}
@end example @end example
The jump back to @code{main-loop} could be achieved by returning through In this case, @code{main-loop} can install an exception handler that
the stack one procedure at a time, using the return value of each would cause any exception raised inside @code{dispatch-key} to print a
procedure to indicate the error condition, but Guile (like most modern warning and jump back to the main loop.
programming languages) provides an additional mechanism called
@dfn{exception handling} that can be used to implement such jumps much The following subsections go into more detail about exception objects,
more conveniently. 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 @menu
* Exception Terminology:: Different ways to say the same thing. * Exception Objects:: What went wrong?
* Catch:: Setting up to catch exceptions. * Raising and Handling Exceptions:: What to do when something goes wrong.
* Throw Handlers:: Handling exceptions before unwinding the stack. * Throw and Catch:: An older approach to exceptions.
* Throw:: Throwing an exception. * Exceptions and C:: Specialized interfaces for C.
* Exception Implementation:: How Guile implements exceptions.
@end menu @end menu
@node Exception Terminology @node Exception Objects
@subsubsection Exception Terminology @subsubsection Exception Objects
There are several variations on the terminology for dealing with When Guile encounters an exceptional situation, it raises an exception,
non-local jumps. It is useful to be aware of them, and to realize where the exception is an object that describes the exceptional
that they all refer to the same basic mechanism. situation. Exception objects are structured data, built on the record
facility (@pxref{Records}).
@itemize @bullet @deftp {Exception Type} &exception
@item The base exception type. All exception objects are composed of
Actually making a non-local jump may be called @dfn{raising an instances of subtypes of @code{&exception}.
exception}, @dfn{raising a signal}, @dfn{throwing an exception} or @end deftp
@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}.
@item @deffn {Scheme Procedure} exception-type? obj
Handling the jump at its target may be referred to as @dfn{catching} or Return true if @var{obj} is an exception type.
@dfn{handling} the @dfn{exception}, @dfn{signal} or, where an error @end deffn
condition is involved, @dfn{error}.
@end itemize
Where @dfn{signal} and @dfn{signalling} are used, special care is needed Exception types exist in a hierarchy. New exception types can be
to avoid the risk of confusion with POSIX signals. defined using @code{make-exception-type}.
This manual prefers to speak of throwing and catching exceptions, since @deffn {Scheme Procedure} make-exception-type id parent field-names
this terminology matches the corresponding Guile primitives. 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 Exception type objects are record type objects, and as such, one can use
@dfn{delimited continuations} (@pxref{Prompts}). In particular, @code{record-constructor} on an exception type to get its constructor.
throwing an exception is akin to invoking an @dfn{escape continuation} The constructor will take as many arguments as the exception has fields
(@pxref{Prompt Primitives, @code{call/ec}}). (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 @node Raising and Handling Exceptions
@subsubsection Catching Exceptions @subsubsection Raising and Handling Exceptions
@code{catch} is used to set up a target for a possible non-local jump. An exception object describes an exceptional situation. To bring that
The arguments of a @code{catch} expression are a @dfn{key}, which description to the attention of the user or to handle the situation
restricts the set of exceptions to which this @code{catch} applies, a programmatically, the first step is to @dfn{raise} the exception.
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.
When an exception is thrown using the @code{throw} function, the first @deffn {Scheme Procedure} raise-exception obj [#:continuable=#f]
argument of the @code{throw} is a symbol that indicates the type of the Raise an exception by invoking the current exception handler on
exception. For example, Guile throws an exception using the symbol @var{obj}. The handler is called with a continuation whose dynamic
@code{numerical-overflow} to indicate numerical overflow errors such as environment is that of the call to @code{raise}, except that the current
division by zero: exception handler is the one that was in place when the handler being
called was installed.
@lisp If @var{continuable?} is true, the handler is invoked in tail position
(/ 1 0) relative to the @code{raise-exception} call. Otherwise if the handler
@result{} returns, a non-continuable exception of type @code{&non-continuable} is
ABORT: (numerical-overflow) raised in the same dynamic environment as the handler.
@end lisp @end deffn
The @var{key} argument in a @code{catch} expression corresponds to this As the above description notes, Guile has a notion of a @dfn{current
symbol. @var{key} may be a specific symbol, such as exception handler}. At the REPL, this exception handler may enter a
@code{numerical-overflow}, in which case the @code{catch} applies recursive debugger; in a standalone program, it may simply print a
specifically to exceptions of that type; or it may be @code{#t}, which representation of the error and exit.
means that the @code{catch} applies to all exceptions, irrespective of
their type.
The second argument of a @code{catch} expression should be a thunk To establish an exception handler within the dynamic extent of a call,
(i.e.@: a procedure that accepts no arguments) that specifies the normal use @code{with-exception-handler}.
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.
The third argument of a @code{catch} expression is a handler procedure. @deffn {Scheme Procedure} with-exception-handler handler thunk @
If an exception is thrown, this procedure is called with exactly the [#:unwind?=#f] [#:unwind-for-type=#t]
arguments specified by the @code{throw}. Therefore, the handler Establish @var{handler}, a procedure of one argument, as the current
procedure must be designed to accept a number of arguments that exception handler during the dynamic extent of invoking @var{thunk}.
corresponds to the number of arguments in all @code{throw} expressions
that can be caught by this @code{catch}.
The fourth, optional argument of a @code{catch} expression is another If @code{raise-exception} is called during the dynamic extent of
handler procedure, called the @dfn{pre-unwind} handler. It differs from invoking @var{thunk}, @var{handler} will be invoked on the argument of
the third argument in that if an exception is thrown, it is called, @code{raise-exception}.
@emph{before} the third argument handler, in exactly the dynamic context @end deffn
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 There are two kinds of exception handlers: unwinding and non-unwinding.
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 By default, exception handlers are non-unwinding. Unless
prevailing @code{catch}. @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] @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_with_pre_unwind_handler (key, thunk, handler, pre_unwind_handler)
@deffnx {C Function} scm_catch (key, thunk, handler) @deffnx {C Function} scm_catch (key, thunk, handler)
Invoke @var{thunk} in the dynamic context of @var{handler} for Establish an exception handler during the dynamic extent of the call to
exceptions matching @var{key}. If thunk throws to the symbol @var{thunk}. @var{key} is either @code{#t}, indicating that all
@var{key}, then @var{handler} is invoked this way: exceptions should be handled, or a symbol, restricting the exceptions
@lisp handled to those having the @var{key} as their @code{exception-kind}.
(handler key args ...)
@end lisp
@var{key} is a symbol or @code{#t}. If @var{thunk} executes normally, meaning without throwing any
exceptions, the handler procedures are not called at all and the result
@var{thunk} takes no arguments. If @var{thunk} returns of the @code{thunk} call is the result of the @code{catch}. Otherwise
normally, that is the return value of @code{catch}. if an exception is thrown that matches @var{key}, @var{handler} is
called with the continuation of the @code{catch} call.
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.
@end deffn @end deffn
If a handler procedure needs to match a variety of @code{throw} Given the discussion from the previous section, it is most precise and
expressions with varying numbers of arguments, you should write it like concise to specify what @code{catch} does by expressing it in terms of
this: @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 @lisp
(lambda (key . args) (catch 'a
@dots{}) (lambda ()
(with-throw-handler 'b
(lambda ()
(catch 'a
(lambda ()
(throw 'b))
inner-handler))
(lambda (key . args)
(throw 'a))))
outer-handler)
@end lisp @end lisp
@noindent @noindent
The @var{key} argument is guaranteed always to be present, because a This code will call @code{inner-handler} and then continue with the
@code{throw} without a @var{key} is not valid. The number and continuation of the inner @code{catch}.
interpretation of the @var{args} varies from one type of exception to
another, but should be specified by the documentation for each exception
type.
Note that, once the normal (post-unwind) handler procedure is invoked, Finally, we get to @code{throw}, which is the older equivalent to
the catch that led to the handler procedure being called is no longer @code{raise-exception}.
active. Therefore, if the handler procedure itself throws an exception,
that exception can only be caught by another active catch higher up the @deffn {Scheme Procedure} throw key arg @dots{}
call stack, if there is one. @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) @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) @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} 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}). Management}).
@end deftypefn @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) @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 The above @code{scm_with_throw_handler} takes Scheme procedures as body
(thunk) and handler arguments. @code{scm_c_with_throw_handler} is an (thunk) and handler arguments. @code{scm_c_with_throw_handler} is an
equivalent taking C functions. See @code{scm_c_catch} (@pxref{Catch}) equivalent taking C functions. See @code{scm_c_catch}
for a description of the parameters, the behaviour however of course (@pxref{Exceptions and C}) for a description of the parameters, the
follows @code{with-throw-handler}. behaviour however of course follows @code{with-throw-handler}.
@end deftypefn @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 @node Error Reporting
@subsection Procedures for Signaling Errors @subsection Procedures for Signaling Errors
@ -2023,8 +2157,13 @@ Guile's parameters conform to SRFI-39 (@pxref{SRFI-39}).
@node Handling Errors @node Handling Errors
@subsection How to Handle Errors @subsection How to Handle Errors
Error handling is based on @code{catch} and @code{throw}. Errors are Guile is currently in a transition from its historical @code{catch} and
always thrown with a @var{key} and four arguments: @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 @itemize @bullet
@item @item

View file

@ -356,8 +356,8 @@ library, or from Guile itself.
@menu @menu
* Catching Exceptions:: Handling errors after the stack is unwound. * 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. * Pre-Unwind Debugging:: Debugging before the exception is thrown.
* Standard Error Handling:: Call-with-error-handling.
* Stack Overflow:: Detecting and handling runaway recursion. * Stack Overflow:: Detecting and handling runaway recursion.
* Debug Options:: A historical interface to debugging. * Debug Options:: A historical interface to debugging.
@end menu @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 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 ``division by zero'' -- and any parameters that the code which signalled
the error chose explicitly to provide. This information originates with the error chose explicitly to provide. This information originates with
the @code{error} or @code{throw} call (or their C code equivalents, if the @code{error} or @code{raise-exception} call (or their C code
the error is detected by C code) that signals the error, and is passed equivalents, if the error is detected by C code) that signals the error,
automatically to the handler procedure of the innermost applicable and is passed automatically to the handler procedure of the innermost
@code{catch} or @code{with-throw-handler} expression. applicable exception handler.
Therefore, to catch errors that occur within a chunk of Scheme code, and Therefore, to catch errors that occur within a chunk of Scheme code, and
to intercept basic information about those errors, you need to execute to intercept basic information about those errors, you need to execute
that code inside the dynamic context of a @code{catch} or that code inside the dynamic context of a @code{with-exception-handler},
@code{with-throw-handler} expression, or the equivalent in C. In Scheme, or the equivalent in C.
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}.
For example, to print out a message and return #f when an error occurs, For example, to print out a message and return #f when an error occurs,
you might use: you might use:
@smalllisp @smalllisp
(define (catch-all thunk) (define (catch-all thunk)
(catch #t (with-exception-handler
thunk (lambda (exn)
(lambda (key . parameters)
(format (current-error-port) (format (current-error-port)
"Uncaught throw to '~a: ~a\n" key parameters) "Uncaught exception: ~s\n" exn)
#f))) #f)
thunk
#:unwind? #t))
(catch-all (catch-all
(lambda () (error "Not a vegetable: tomato"))) (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 @result{} #f
@end smalllisp @end smalllisp
The @code{#t} means that the catch is applicable to all kinds of error. @xref{Exceptions}, for full details.
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:
@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 @node Pre-Unwind Debugging
@subsubsection Pre-Unwind Debugging @subsubsection Pre-Unwind Debugging
Instead of saving a stack away and waiting for the @code{catch} to Sometimes when something goes wrong, what you want is not just a
return, you can handle errors directly, from within the pre-unwind representation of the exceptional situation, but the context that
handler. 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 The most basic idea would be to simply print a backtrace:
to use a procedure like this:
@lisp @example
(define (with-backtrace thunk) (define (call-with-backtrace thunk)
(with-throw-handler #t (with-exception-handler
thunk (lambda (exn)
(lambda args (backtrace)))) (backtrace)
(with-backtrace (lambda () (error "Not a vegetable: tomato"))) (raise-exception exn))
@end lisp thunk))
@end example
Since we used @code{with-throw-handler} here, we didn't actually catch Here we use the built-in @code{backtrace} procedure to print the
the error. @xref{Throw Handlers}, for more information. However, we did backtrace.
print out a context at the time of the error, using the built-in
procedure, @code{backtrace}.
@deffn {Scheme Procedure} backtrace [highlights] @deffn {Scheme Procedure} backtrace [highlights]
@deffnx {C Function} scm_backtrace_with_highlights (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. will be highlighted wherever they appear in the backtrace.
@end deffn @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) 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 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 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] [#:trap-handler trap-handler='debug]
Call a thunk in a context in which errors are handled. 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: There are five keyword arguments:
@table @var @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 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 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 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 system, but the user might have exception handlers in place
Handlers}) that want to run before the stack is unwound, and we don't (@pxref{Raising and Handling Exceptions}) that want to run before the
have any stack in which to run them. 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 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 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. be able to get a backtrace from any pre-unwind handler.

View file

@ -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 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 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 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 you unlock the mutex ``on the way out'', via an exception handler or
@code{dynamic-wind}. @xref{Catch}, and @xref{Dynamic Wind}. @code{dynamic-wind}. @xref{Exceptions}, and @xref{Dynamic Wind}.
But if you arrange to unlock the mutex when leaving a dynamic extent via 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 @code{dynamic-wind}, what to do if control re-enters that dynamic extent

View file

@ -1139,38 +1139,16 @@ descriptor @var{rtd} (and not any of its sub- or supertypes) is mutable.
@subsubsection rnrs exceptions @subsubsection rnrs exceptions
The @code{(rnrs exceptions (6))} library provides functionality related The @code{(rnrs exceptions (6))} library provides functionality related
to signaling and handling exceptional situations. This functionality is to signaling and handling exceptional situations. This functionality
similar to the exception handling systems provided by Guile's core re-exports Guile's core exception-handling primitives.
library @xref{Exceptions}, and by the SRFI-18 and SRFI-34 @xref{Exceptions}, for a full discussion. @xref{SRFI-34}, for a similar
modules---@xref{SRFI-18 Exceptions}, and @ref{SRFI-34}, pre-R6RS facility. In Guile, SRFI-34, SRFI-35, and R6RS exception
respectively---but there are some key differences in concepts and handling are all built on the same core facilities, and so are
behavior. interoperable.
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.
@deffn {Scheme Procedure} with-exception-handler handler thunk @deffn {Scheme Procedure} with-exception-handler handler thunk
Installs @var{handler}, which must be a procedure taking one argument, @xref{Raising and Handling Exceptions}, for more information on
as the current exception handler during the invocation of @var{thunk}, a @code{with-exception-handler}.
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}).
@end deffn @end deffn
@deffn {Scheme Syntax} guard (variable clause1 clause2 ...) body @deffn {Scheme Syntax} guard (variable clause1 clause2 ...) body
@ -1194,17 +1172,18 @@ evaluates to @code{baz}.
@end deffn @end deffn
@deffn {Scheme Procedure} raise obj @deffn {Scheme Procedure} raise obj
Raises a non-continuable exception by invoking the currently-installed Equivalent to core Guile @code{(raise-exception @var{obj})}.
exception handler on @var{obj}. If the handler returns, a @xref{Raising and Handling Exceptions}. p(Unfortunately, @code{raise}
@code{&non-continuable} exception will be raised in the dynamic context is already bound to a different function in core Guile.
in which the handler was installed. @xref{Signals}.)
@end deffn @end deffn
@deffn {Scheme Procedure} raise-continuable obj @deffn {Scheme Procedure} raise-continuable obj
Raises a continuable exception by invoking currently-installed exception Equivalent to core Guile @code{(raise-exception @var{obj} #:continuable?
handler on @var{obj}. #t)}. @xref{Raising and Handling Exceptions}.
@end deffn @end deffn
@node rnrs conditions @node rnrs conditions
@subsubsection 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 return the requisite fields from the first component simple condition
found to be of the appropriate type. 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 This library is quite similar to the SRFI-35 conditions module
(@pxref{SRFI-35}). Among other minor differences, the (@pxref{SRFI-35}). Among other minor differences, the @code{(rnrs
@code{(rnrs conditions)} library features slightly different semantics conditions)} library features slightly different semantics around
around condition field accessors, and comes with a larger number of condition field accessors, and comes with a larger number of pre-defined
pre-defined condition types. The two APIs are not currently compatible, condition types. The two APIs are compatible; the @code{condition?}
however; the @code{condition?} predicate from one API will return predicate from one API will return @code{#t} when applied to a condition
@code{#f} when applied to a condition object created in the other. object created in the other. of the condition types are the same,
also.
@deffn {Condition Type} &condition @deffn {Condition Type} &condition
@deffnx {Scheme Procedure} condition? obj @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 @end deffn
@deffn {Scheme Procedure} condition condition1 ... @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} make-serious-condition
@deffnx {Scheme Procedure} serious-condition? obj @deffnx {Scheme Procedure} serious-condition? obj
A base type for conditions representing errors serious enough that 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 @end deffn
@deffn {Condition Type} &error @deffn {Condition Type} &error
@deffnx {Scheme Procedure} make-error @deffnx {Scheme Procedure} make-error
@deffnx {Scheme Procedure} error? obj @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 @end deffn
@deffn {Condition Type} &violation @deffn {Condition Type} &violation
@deffnx {Scheme Procedure} make-violation @deffnx {Scheme Procedure} make-violation
@deffnx {Scheme Procedure} violation? @deffnx {Scheme Procedure} violation?
A subtype of @code{&serious} that can be used to represent violations A subtype of @code{&serious} that can be used to represent violations of
of a language or library standard. a language or library standard. Known as @code{&programming-error} in
core Guile.
@end deffn @end deffn
@deffn {Condition Type} &assertion @deffn {Condition Type} &assertion
@deffnx {Scheme Procedure} make-assertion-violation @deffnx {Scheme Procedure} make-assertion-violation
@deffnx {Scheme Procedure} assertion-violation? obj @deffnx {Scheme Procedure} assertion-violation? obj
A subtype of @code{&violation} that indicates an invalid call to a A subtype of @code{&violation} that indicates an invalid call to a
procedure. procedure. Known as @code{&assertion-failure} in core Guile.
@end deffn @end deffn
@deffn {Condition Type} &irritants @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} make-undefined-violation
@deffnx {Scheme Procedure} undefined-violation? obj @deffnx {Scheme Procedure} undefined-violation? obj
A subtype of @code{&violation} that indicates a reference to an unbound A subtype of @code{&violation} that indicates a reference to an unbound
identifier. identifier. Known as @code{&undefined-variable} in core Guile.
@end deffn @end deffn
@node R6RS I/O Conditions @node R6RS I/O Conditions

View file

@ -1,6 +1,6 @@
@c -*-texinfo-*- @c -*-texinfo-*-
@c This is part of the GNU Guile Reference Manual. @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 Free Software Foundation, Inc.
@c See the file guile.texi for copying conditions. @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)) (use-modules (srfi srfi-34))
@end lisp @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 @node SRFI-35
@ -3330,7 +3349,7 @@ handling mechanisms} as an alternative to its own built-in mechanisms
@cindex conditions @cindex conditions
@cindex exceptions @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 @dfn{conditions}, a data structure akin to records designed to convey
information about exceptional conditions between parts of a program. It information about exceptional conditions between parts of a program. It
is normally used in conjunction with SRFI-34's @code{raise}: 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. Return true if @var{c} is of type @code{&error} or one of its subtypes.
@end deffn @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 @node SRFI-37
@subsection SRFI-37 - args-fold @subsection SRFI-37 - args-fold
@cindex SRFI-37 @cindex SRFI-37