summaryrefslogtreecommitdiff
path: root/doc/ref/api-control.texi
diff options
context:
space:
mode:
authorAndy Wingo <wingo@pobox.com>2019-11-13 22:26:31 +0100
committerAndy Wingo <wingo@pobox.com>2019-11-13 22:33:09 +0100
commit44ee8c5559ed2f30df464ba1bffdae24994291b3 (patch)
treea67d0142a23dcdf502ae96c6299004e726a7c8ab /doc/ref/api-control.texi
parentf4ca107f7fe0b6f1ca2c03b558f16077fc89db04 (diff)
downloadguile-44ee8c5559ed2f30df464ba1bffdae24994291b3.tar.gz
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.
Diffstat (limited to 'doc/ref/api-control.texi')
-rw-r--r--doc/ref/api-control.texi813
1 files changed, 476 insertions, 337 deletions
diff --git a/doc/ref/api-control.texi b/doc/ref/api-control.texi
index c0d1c1800..f603e32e3 100644
--- a/doc/ref/api-control.texi
+++ b/doc/ref/api-control.texi
@@ -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,298 +1025,423 @@ 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.
-@node Catch
-@subsubsection Catching Exceptions
+@deffn {Scheme Procedure} make-exception exceptions @dots{}
+Return an exception object composed of @var{exceptions}.
+@end deffn
-@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.
+@deffn {Scheme Procedure} exception? obj
+Return true if @var{obj} is an exception object.
+@end deffn
-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} 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
-@lisp
-(/ 1 0)
-@result{}
-ABORT: (numerical-overflow)
-@end lisp
+@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
-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.
-
-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.
-
-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}.
-
-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}.
+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:
-@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
+@example
+(use-modules (ice-9 exceptions))
+@end example
-@var{key} is a symbol or @code{#t}.
+The following diagram gives an overview of the standard exception type
+hierarchy.
-@var{thunk} takes no arguments. If @var{thunk} returns
-normally, that is the return value of @code{catch}.
+@example
+&exception
+|- &warning
+|- &message
+|- &irritants
+|- &origin
+\- &error
+ |- &external-error
+ \- &programming-error
+ |- &assertion-failure
+ |- &non-continuable
+ |- &implementation-restriction
+ |- &lexical
+ |- &syntax
+ \- &undefined-variable
+@end example
-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.
+@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
-If the key is @code{#t}, then a throw to @emph{any} symbol will
-match this call to @code{catch}.
+@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
-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.
+@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
-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.
+@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
-If a handler procedure needs to match a variety of @code{throw}
-expressions with varying numbers of arguments, you should write it like
-this:
+@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
-@lisp
-(lambda (key . args)
- @dots{})
-@end lisp
+@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
-@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.
-
-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.
+@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
-@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}
-take Scheme procedures as body and handler arguments.
-@code{scm_c_catch} and @code{scm_internal_catch} are equivalents taking
-C functions.
+@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
-@var{body} is called as @code{@var{body} (@var{body_data})} with a catch
-on exceptions of the given @var{tag} type. If an exception is caught,
-@var{pre_unwind_handler} and @var{handler} are called as
-@code{@var{handler} (@var{handler_data}, @var{key}, @var{args})}.
-@var{key} and @var{args} are the @code{SCM} key and argument list from
-the @code{throw}.
+@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
-@tpindex scm_t_catch_body
-@tpindex scm_t_catch_handler
-@var{body} and @var{handler} should have the following prototypes.
-@code{scm_t_catch_body} and @code{scm_t_catch_handler} are pointer
-typedefs for these.
+@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
-@example
-SCM body (void *data);
-SCM handler (void *data, SCM key, SCM args);
-@end example
+@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
-The @var{body_data} and @var{handler_data} parameters are passed to
-the respective calls so an application can communicate extra
-information to those functions.
+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
-If the data consists of an @code{SCM} object, care should be taken that
-it isn't garbage collected while still required. If the @code{SCM} is a
-local C variable, one way to protect it is to pass a pointer to that
-variable as the data parameter, since the C compiler will then know the
-value must be held on the stack. Another way is to use
-@code{scm_remember_upto_here_1} (@pxref{Foreign Object Memory
-Management}).
-@end deftypefn
+@node Raising and Handling Exceptions
+@subsubsection Raising and Handling Exceptions
-@node Throw Handlers
-@subsubsection Throw Handlers
+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.
-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} 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.
-@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}.
+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
+
+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.
-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.
+To establish an exception handler within the dynamic extent of a call,
+use @code{with-exception-handler}.
+
+@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}.
+
+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
-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.
+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}.
-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,
+@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)
+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}.
+
+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
-@lisp
-(with-throw-handler 'key
- (lambda () @dots{} (throw 'key args @dots{}) @dots{})
- handler)
-@end lisp
+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:
-@noindent
-is mostly equivalent to
+@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
-@lisp
-((lambda () @dots{} (handler 'key args @dots{}) @dots{}))
-@end lisp
+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.
-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}.)
+If @code{catch} is called with four arguments, then the use of
+@var{thunk} should be replaced with:
-@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}.
-@end deftypefn
+@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.
-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}.
+@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
@@ -1328,101 +1462,101 @@ Here is an example to illustrate this behavior:
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.
+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)
-Invoke the catch form matching @var{key}, passing @var{arg} @dots{} to
-the @var{handler}.
+Raise an exception with kind @var{key} and arguments @var{args}.
+@var{key} is a symbol, denoting the ``kind'' of the exception.
+@end deffn
-@var{key} is a symbol. It will match catches of the same symbol or of
-@code{#t}.
+Again, we can specify what @code{throw} does by expressing it in terms
+of @code{raise-exception}.
-If there is no handler at all, Guile prints an error and then exits.
+@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
-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}.
+@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
-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:
+@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
-@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:
+@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.
+
+@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}
+take Scheme procedures as body and handler arguments.
+@code{scm_c_catch} and @code{scm_internal_catch} are equivalents taking
+C functions.
+
+@var{body} is called as @code{@var{body} (@var{body_data})} with a catch
+on exceptions of the given @var{tag} type. If an exception is caught,
+@var{pre_unwind_handler} and @var{handler} are called as
+@code{@var{handler} (@var{handler_data}, @var{key}, @var{args})}.
+@var{key} and @var{args} are the @code{SCM} key and argument list from
+the @code{throw}.
+
+@tpindex scm_t_catch_body
+@tpindex scm_t_catch_handler
+@var{body} and @var{handler} should have the following prototypes.
+@code{scm_t_catch_body} and @code{scm_t_catch_handler} are pointer
+typedefs for these.
@example
-$ guile -c "(begin (throw 'badex) (display \"here\\n\"))"
-guile: uncaught throw to badex: ()
-$
+SCM body (void *data);
+SCM handler (void *data, SCM key, SCM args);
@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.
+The @var{body_data} and @var{handler_data} parameters are passed to
+the respective calls so an application can communicate extra
+information to those functions.
+
+If the data consists of an @code{SCM} object, care should be taken that
+it isn't garbage collected while still required. If the @code{SCM} is a
+local C variable, one way to protect it is to pass a pointer to that
+variable as the data parameter, since the C compiler will then know the
+value must be held on the stack. Another way is to use
+@code{scm_remember_upto_here_1} (@pxref{Foreign Object Memory
+Management}).
+@end deftypefn
+
+@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{Exceptions and C}) for a description of the parameters, the
+behaviour however of course follows @code{with-throw-handler}.
+@end deftypefn
@node Error Reporting
@@ -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