mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-06-11 14:21:10 +02:00
(Throw Handlers): New node.
(Throw): Moved to after the Lazy Catch node. (Catch): Enhance to cover the optional pre-unwind handler, and new APIs scm_c_catch, scm_catch_with_pre_unwind_handler. (Lazy Catch): Describe relationship to with-throw-handler. Document that the handler may return, and what happens if it does. (Throw): Mention that a throw can be handled by a throw handler as well as by a catch.
This commit is contained in:
parent
ba28d59428
commit
7b4c914e49
2 changed files with 228 additions and 86 deletions
|
@ -1,3 +1,14 @@
|
|||
2006-02-04 Neil Jerram <neil@ossau.uklinux.net>
|
||||
|
||||
* api-control.texi (Throw Handlers): New node.
|
||||
(Throw): Moved to after the Lazy Catch node.
|
||||
(Catch): Enhance to cover the optional pre-unwind handler, and new
|
||||
APIs scm_c_catch, scm_catch_with_pre_unwind_handler.
|
||||
(Lazy Catch): Describe relationship to with-throw-handler.
|
||||
Document that the handler may return, and what happens if it does.
|
||||
(Throw): Mention that a throw can be handled by a throw handler as
|
||||
well as by a catch.
|
||||
|
||||
2006-02-04 Kevin Ryde <user42@zip.com.au>
|
||||
|
||||
* api-options.texi (Build Config): pxref libtool on libguileinterface
|
||||
|
|
|
@ -600,8 +600,9 @@ more conveniently.
|
|||
@menu
|
||||
* Exception Terminology:: Different ways to say the same thing.
|
||||
* Catch:: Setting up to catch exceptions.
|
||||
* Throw:: Throwing an exception.
|
||||
* Throw Handlers:: Adding extra handling to a throw.
|
||||
* Lazy Catch:: Catch without unwinding the stack.
|
||||
* Throw:: Throwing an exception.
|
||||
* Exception Implementation:: How Guile implements exceptions.
|
||||
@end menu
|
||||
|
||||
|
@ -640,11 +641,11 @@ this terminology matches the corresponding Guile primitives.
|
|||
@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 a @dfn{handler} procedure
|
||||
that says what to do if an exception is thrown while executing the code.
|
||||
Note that if the execution thunk executes @dfn{normally}, which means
|
||||
without throwing any exceptions, the handler procedure is not called at
|
||||
all.
|
||||
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
|
||||
argument of the @code{throw} is a symbol that indicates the type of the
|
||||
|
@ -679,7 +680,18 @@ procedure must be designed to accept a number of arguments that
|
|||
corresponds to the number of arguments in all @code{throw} expressions
|
||||
that can be caught by this @code{catch}.
|
||||
|
||||
@deffn {Scheme Procedure} catch key thunk handler
|
||||
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}.
|
||||
|
||||
@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
|
||||
|
@ -699,9 +711,24 @@ 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
|
||||
|
||||
If the handler procedure needs to match a variety of @code{throw}
|
||||
If a handler procedure needs to match a variety of @code{throw}
|
||||
expressions with varying numbers of arguments, you should write it like
|
||||
this:
|
||||
|
||||
|
@ -717,24 +744,26 @@ 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 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.
|
||||
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.
|
||||
|
||||
@sp 1
|
||||
@deftypefn {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} takes Scheme procedures as body and handler
|
||||
arguments. @code{scm_internal_catch} is an equivalent taking C
|
||||
functions.
|
||||
@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{handler} is called @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}.
|
||||
@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
|
||||
|
@ -761,73 +790,58 @@ Operations}).
|
|||
@end deftypefn
|
||||
|
||||
|
||||
@node Throw
|
||||
@subsubsection Throwing Exceptions
|
||||
@node Throw Handlers
|
||||
@subsubsection Throw Handlers
|
||||
|
||||
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.
|
||||
It's sometimes useful to be able to intercept an exception that is being
|
||||
thrown, but without changing where in the dynamic context that exception
|
||||
will eventually be caught. This could be to clean up some related state
|
||||
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} throw key . args
|
||||
@deffnx {C Function} scm_throw (key, args)
|
||||
Invoke the catch form matching @var{key}, passing @var{args} 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.
|
||||
@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
|
||||
|
||||
When an exception is thrown, it will be caught by the innermost
|
||||
@code{catch} expression that applies to the type of the thrown
|
||||
exception; in other words, the innermost @code{catch} whose @var{key} is
|
||||
@code{#t} or is the same symbol as that used in the @code{throw}
|
||||
expression. Once Guile has identified the appropriate @code{catch}, it
|
||||
handles the exception by applying that @code{catch} expression's handler
|
||||
procedure to the arguments of the @code{throw}.
|
||||
@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
|
||||
|
||||
If there is no appropriate @code{catch} 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:
|
||||
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.
|
||||
|
||||
@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.
|
||||
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.
|
||||
|
||||
|
||||
@node Lazy Catch
|
||||
@subsubsection Catch Without Unwinding
|
||||
|
||||
Before version 1.8, Guile's closest equivalent to
|
||||
@code{with-throw-handler} was @code{lazy-catch}. From version 1.8
|
||||
onwards we recommend using @code{with-throw-handler} because its
|
||||
behaviour is more useful than that of @code{lazy-catch}, but
|
||||
@code{lazy-catch} is still supported as well.
|
||||
|
||||
A @dfn{lazy catch} is used in the same way as a normal @code{catch},
|
||||
with @var{key}, @var{thunk} and @var{handler} arguments specifying the
|
||||
exception type, normal case code and handler procedure, but differs in
|
||||
|
@ -839,8 +853,10 @@ that caused the handler to be invoked.
|
|||
@deffnx {C Function} scm_lazy_catch (key, thunk, handler)
|
||||
This behaves exactly like @code{catch}, except that it does
|
||||
not unwind the stack before invoking @var{handler}.
|
||||
The @var{handler} procedure is not allowed to return:
|
||||
it must throw to another catch, or otherwise exit non-locally.
|
||||
If the @var{handler} procedure returns normally, Guile
|
||||
rethrows the same exception again to the next innermost catch,
|
||||
lazy-catch or throw handler. If the @var{handler} exits
|
||||
non-locally, that exit determines the continuation.
|
||||
@end deffn
|
||||
|
||||
@deftypefn {C Function} SCM scm_internal_lazy_catch (SCM tag, scm_t_catch_body body, void *body_data, scm_t_catch_handler handler, void *handler_data)
|
||||
|
@ -851,11 +867,9 @@ a description of the parameters, the behaviour however of course
|
|||
follows @code{lazy-catch}.
|
||||
@end deftypefn
|
||||
|
||||
Typically, @var{handler} should save any desired state associated with
|
||||
the stack at the point where the corresponding @code{throw} occurred,
|
||||
and then throw an exception itself --- usually the same exception as the
|
||||
one it caught. If @var{handler} is invoked and does @emph{not} throw an
|
||||
exception, Guile itself throws an exception with key @code{misc-error}.
|
||||
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 caught
|
||||
by a @code{lazy-catch} is @emph{almost} equivalent to calling the
|
||||
|
@ -926,6 +940,123 @@ debuggers and other reflective programming tools that need to access the
|
|||
state of the call stack at the exact point where an exception or an
|
||||
error is thrown. For an example of this, see REFFIXME:stack-catch.
|
||||
|
||||
It should be obvious from the above that @code{lazy-catch} is very
|
||||
similar to @code{with-throw-handler}. In fact Guile implements
|
||||
@code{lazy-catch} in exactly the same way as @code{with-throw-handler},
|
||||
except with a flag set to say ``where there are slight differences
|
||||
between what @code{with-throw-handler} and @code{lazy-catch} would do,
|
||||
do what @code{lazy-catch} has always done''. There are two such
|
||||
differences:
|
||||
|
||||
@enumerate
|
||||
@item
|
||||
@code{with-throw-handler} handlers execute in the full dynamic context
|
||||
of the originating @code{throw} call. @code{lazy-catch} handlers
|
||||
execute in the dynamic context of the @code{lazy-catch} expression,
|
||||
excepting only that the stack has not yet been unwound from the point of
|
||||
the @code{throw} call.
|
||||
|
||||
@item
|
||||
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 _closer_
|
||||
to the throw than the first @code{with-throw-handler}. If a
|
||||
@code{lazy-catch} handler throws, it will always be handled by a
|
||||
@code{catch} or throw handler that is higher up the dynamic context than
|
||||
the first @code{lazy-catch}.
|
||||
@end enumerate
|
||||
|
||||
Here is an example to illustrate the second difference:
|
||||
|
||||
@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}. If the
|
||||
@code{with-throw-handler} was changed to @code{lazy-catch}, however, the
|
||||
code would call @code{outer-handler} and then continue with the
|
||||
continuation of the outer @code{catch}.
|
||||
|
||||
Modulo these two differences, any statements in the previous and
|
||||
following subsections about throw handlers apply to lazy catches as
|
||||
well.
|
||||
|
||||
|
||||
@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 . args
|
||||
@deffnx {C Function} scm_throw (key, args)
|
||||
Invoke the catch form matching @var{key}, passing @var{args} 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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue