Gauche’s exception system consists of three components; (1) the way to signal an exceptional case has occurred, (2) the way to specify how to handle such a case, and (3) the standard objects (conditions) to communicate the code that signals an exceptional case and the code that handles it.
Those three components are typically used together, so first we explain the typical usage patterns using examples. Then we describe each feature in detail.
Note for terminology: some languages use the word exception to refer to an object used to communicate the code that encountered an exceptional situation with a handler that deals with it. Gauche uses a term condition to refer to such objects, following SRFI-35. Exception is the situation, and condition is a runtime object that describes it.
|• Exception handling overview:|
|• Signaling exceptions:|
|• Handling exceptions:|
One of the most typical exception handling is to catch a
specific error raised by some built-in or library procedures.
guard can be used for such a purpose. The code
looks like this:
(guard (exc [(condition-has-type? exc <read-error>) (format #t "read error!") 'read-error] [else 'other-error]) (read-from-string "(abc"))
The cadr of
guard clause is a form of
(variable clause …). In this example,
the variable is
exc, and it has two clauses.
Each clause has the form like the one in
The cddr of
guard is the body, a list of expressions. This
example has only one expression,
guard starts executing its body.
read-from-string raises an error of type
when it encounters syntactic errors. The form
the error, and binds the condition object to the variable exc,
then checks the clauses following exc in a similar manner
cond—in this case, the thrown condition is of type
<read-error>, so the test of the first clause is satisfied,
and the rest of clause is executed, i.e.
"read error!" is
printed and a symbol
read-error is returned.
If you’re familiar with other languages, you may recognize the
pattern. The cddr of
guard form is like try clause
of C++/Java or the cadr of
handler-case of Common Lisp;
and the cdadr of
guard form is like
or the cddr of
In the test expressions it is common to check the type of
thrown condition. The function
defined in SRFI-35 but it’s rather lengthy. Gauche’s condition
classes can also work like a predicate, so you can write the
above expression like this.
(guard (exc [(<read-error> exc) (format #t "read error!") 'read-error] [else 'other-error]) (read-from-string "(abc"))
Note: Generally you can’t use
is-a? to test if
the thrown condition is of a specific type, since a condition
may be compound. See Conditions about compound
If no tests of clauses satisfy and no
else clause is given,
the exception ‘falls off’ the
guard construct, i.e.
it will be handled by the outer level of
guard form or
top-level. For example, the following
if the body throws other type of conditions, it must be handled
by outer level.
(guard (exc [(<read-error> exc) (handle-read-error)] [(<system-error> exc) (handle-system-error)]) body …)
See Handling exceptions for more details on
other lower-level exception handling constructs.
The generic way to signal an exception is to use
You can pass any object to condition; its interpretation
solely depends on the exception handler. If you know the code
raises an integer as a condition, you can catch it by
(guard (exc [(integer? exc) 'raised]) (raise 3))
However, as a convention, it is preferable to use an instance
<condition> or one of its subclasses. A macro
can be used to create a condition object. The following examples
show how to create a condition with some slot values and then raise it.
;; create and raise an error condition (raise (condition (<error> (message "An error occurred.")))) ;; create and raise a system error condition (raise (condition (<system-error> (message "A system error occurred.") (errno EINTR))))
See Conditions for the details of
and what kind of condition classes are provided.
The most common type of condition is an error condition, so
a convenience procedure
provided. They create an error condition with a message and
;; `error' concatenates the arguments into a message. (unless (integer? obj) (error "Integer expected, but got:" obj)) ;; `errorf' uses format to create a message. (unless (equal? x y) (errorf "~s and ~s don't match" x y))
Unlike the exception throwing constructs in some languages,
throw of C++/Java, which abandons its continuation,
raise may return to its caller. If you don’t
raise to return, a rule of thumb is always to pass
one of error conditions to it; then Gauche guarantees
wont return. See the description of
Signaling exceptions for more details.
Note: R7RS adopted slightly different semantics; it splits
raise-continuable, the former is for
noncontinuable exception (if the exception handler returns,
it raises another error), and the latter is for continuable
exception. When you’re in R7RS environment, R7RS-compatible
raise will be used instead of this
You can also define your own condition classes to pass application-specific information from the point of raising exception to the handlers.
To fit to Gauche’s framework (SRFI-35), it is desirable that
the new condition class inherits a built-in
or one of its descendants, and also is an instance of a metaclass
One way of ensuring the above convention as well as increasing
portability is to use
define-condition-type macro, defined
(define-condition-type <myapp-error> <error> myapp-error? (debug-info myapp-error-debug-info) (reason myapp-error-reason))
This defines a condition type (which is a class in Gauche)
<myapp-error>, with a predicate
and slots with accessors. Then you can use the new
condition type like the following code:
(guard (exc [(myapp-error? exc) (let ([debug-info (myapp-error-debug-info exc)] [reason (myapp-error-reason exc)]) ... handle myapp-error ...)]) ... ... (if (something-went-wrong) (raise (condition (<myapp-error> (debug-info "during processing xxx") (reason "something went wrong"))))) ... ... )
If you don’t mind to lose srfi compatibility, you can use
errorf procedures to write
more concise code to raise a condition of subtype of
(if (something-went-wrong) (error <myapp-error> :debug-info "during processing xxx" :reason "something went wrong"))
See the description of
for how the condition type is implemented in Gauche’s object system.
The most common case of exceptions is an error.
Two convenience functions to signal an error condition
in simple cases are provided.
To signal a compound condition, you can use
raise as explained below.
Signals an error. The first form creates an
condition, with a message consists of string and arg …,
and raises it. It is compatible to R7RS and SRFI-23’s
gosh> (define (check-integer x) (unless (integer? x) (error "Integer required, but got:" x))) check-integer gosh> (check-integer "a") *** ERROR: Integer required, but got: "a" Stack Trace: _______________________________________
The second form can be used to raise an error other than the
<error> condition. condition-type must be
a condition type (see Conditions for more explanation of
condition types). It may be followed by keyword-value list
to initialize the condition slots, and then optionally followed by
a string and other objects that becomes an error message.
(define-condition-type <my-error> <error> #f (reason) (priority)) ... (unless (memq operation *supported-operations*) (error <my-error> :reason 'not-supported :priority 'urgent "Operation not supported:" operation)) ...
error, but the error message is formatted by
format, i.e. the first form is equivalent to:
(define (errorf fmt . args) (error (apply format #f fmt args)))
The second form can be used to raise an error other than an
<error> condition. Meaning of condition-type and
keyword-args are the same as
[SRFI-18][R7RS base] This is the base mechanism of signaling exceptions.
The procedure invokes the current exception handler.
The argument condition represents the nature of the exception,
and passed to the exception handler.
Gauche’s built-in and library functions always use
an instance of
<condition> or one of its subclasses as condition,
but you can pass any Scheme object to
raise. The interpretation
of condition is up to the exception handler.
Note: Unlike some of the mainstream languages in which
"throwing" an exception never returns, you can set up an exception
handler in the way that
raise may return. The details
are explained in Handling exceptions.
If you don’t want
raise to return, the best way is to
pass a condition which is an instance of
or one of its subclasses. Gauche’s internal mechanism
guarantees raising such an exception won’t return.
See Conditions for the hierarchy of built-in conditions.
R7RS adopted slightly different semantics regarding returning from
raise; in R7RS,
raise never returns—if the exception
handler returns, another exception is raised.
raise-continuable to explicitly allow returning from
the exception handler. For portable programs, always
<serious-condition> or its subclasses to
[R7RS base] This is the high-level form to handle errors in Gauche.
var is a symbol, and clauses are the same form as
cond’s clauses, i.e. each clause can be either one of
the following forms:
(test expr …)
(test => proc)
The last clause may be
(else expr …).
This form evaluates body … and returns the value(s) of the last body expression in normal case. If an exception is raised during the evaluation of body expressions, the raised exception is bound to a variable var, then evaluates test expression of each clause. If one of test expressions returns true value, then the corresponding exprs are evaluated if the clause is the first form above, or an proc is evaluated and the result of test is passed to the procedure proc if the clause is the second form.
When the test(s) and expr(s) in the clauses are evaluated,
the exception handler that is in effect of the caller of
installed; that is, if an exception is raised again within clauses,
it is handled by the outer exception handler or
If no test returns true value and the last
else clause, then the associated exprs are evaluated.
If no test returns true value and there’s no
the raised exception is re-raised, to be handled by the outer exception
When the exception is handled by one of
returns the value(s) of the last expr in the handling clause.
The clauses are evaluated in the same dynamic environment as
guard form, i.e. any
are unwound before evaluation of the clauses. It is different
from the lower level forms
handler is evaluated before the dynamic environment are unwound.
(let ([z '()]) (guard (e [else (push! z 'caught)]) (dynamic-wind (lambda () (push! z 'pre)) (lambda () (error "foo")) (lambda () (push! z 'post)))) (reverse z)) ⇒ (pre post caught) (guard (e [else (print 'OUTER) #f]) (with-output-to-string (lambda () (print 'INNER) (error "foo")))) ⇒ prints OUTER to the current output port of guard, not to the string port.
Executes expr, then executes cleanups, and
returns the result(s) of expr. If an uncontinuable exception is raised
within expr, cleanups are executed before the
exception escapes from the
For example, the following code calls
stop-motor in order if
everything goes ok, and if anything goes wrong in
stop-motor is still called
before the exception escapes
(unwind-protect (begin (start-motor) (drill-a-hole)) (stop-motor))
The cleanup forms are evaluated in the same dynamic environment
If an exception is thrown within cleanup, it will be
handled outside of the
Although this form looks similar to
work at different layers and should not be confused.
dynamic-wind is the bottom-level
building block and used to manage current exception handlers,
current i/o ports, parameters, etc.
dynamic-wind’s before and after thunks are
called whenever any of those control flow transition occurs.
On the other hand,
unwind-protect only cares about
the Gauche’s exception system.
is called only when expr exits normally or
throws Gauche’s exception.
In the above example, if control escapes from
by calling a continuation captured outside of
cleanup is not called; because the control may return to
drill-a-hole again. It can happen if user-level thread
system is implemented by
call/cc, for example.
You can go back to the body expr
from outside of
as long as cleanup hasn’t been executed. Once cleanup is
executed (more precisely, once execution of cleanup is started),
returning back to the body is prohibited—attempting to do so will
raise an error.
In the above example, once you stop a motor, you can’t restart
drill-a-hole. Reasonable, right?
The following example shows what happens if you try to reenter the body after cleanup is executed:
gosh> (define k #f) k gosh> (unwind-protect (begin (let/cc c (set! k c)) (print "body")) (print "cleanup")) body cleanup #<undef> gosh> (k 0) *** ERROR: Attempt to reenter obsoleted dynamic environment Stack Trace: _______________________________________ 0 (error "Attempt to reenter obsoleted dynamic environment") [unknown location] 1 (eval expr env) at "../lib/gauche/interactive.scm":282
The name of this form is taken from Common Lisp. Some Scheme
systems have similar macros in different names, such as
Makes handler the active error handler and executes thunk.
If thunk returns normally, the result(s) will be returned.
If an error is signaled during execution of thunk,
handler is called with one argument, an exception object
representing the error, with the continuation of
with-error-handler returns whatever value(s) handler
If handler signals an error, it will be handled by the
handler installed when
The dynamic environment where handler is executed is
the same as the error occurs. If
dynamic-wind is used
in thunk, its after method is called after handler
has returned, and before
Note: Using this procedure directly is no longer recommended, since
guard is more safe and portable. We’ll keep this for
a while for the backward compatibility, but we recommend to rewrite
code to use
guard instead of this.
The common idiom of "cleanup on error"
(with-error-handler (lambda (e) (cleanup) (raise e)) (lambda () body …))
should be written like this:
(guard (e [else (cleanup) (raise e)]) body …)
If an exception is raised where no program-defined exception handler is installed, the following action is taken.
<uncaught-exception>condition and stored in the thread object. If other thread calls
thread-join!to retrieve result, the the
<uncaught-exception>is thrown in that thread. Note that no messages are displayed when the original uncaught exception is thrown. See Thread programming tips, for the details.
The default error message and stack trace in the above case 2 and case 3
is printed by
report-error procedure. You can use it
in your error handler if you need the same information.
Prints type and message of a thrown condition object exn, then print the current stack trace. This is the procedure the system calls when you see an error reported on REPL.
Since you can
raise any object, exn can be any
object; it’s not needed to be an instance of
A suitable message is chosen by
You can specify where the output goes by the optional sink
argument: If it is an output port, the output goes there; you can
#t for the current output port and
the output string port, just like
format. That is, when
#f, the message goes to a temporary output string
port, and gathered string is returned. For all the other cases,
an undefined value is returned. If sink is omitted or
any other object listed above, the current error port is used.
Note: As of 0.9.5, this procedure prints stack trace of
the context where
report-error is called, rather than
the context where exn is thrown. It doesn’t matter much
as far as you call
report-error directly inside the
error handler, but in general what
you want to print is the latter, and we have a plan to
attach stack trace info to
<condition> object in future.
This layer provides SRFI-18 compatible simple exception mechanism.
You can override the behavior of higher-level constructs such as
with-error-handler by using
Note that it is a double-edged sword. You’ll get a freedom to construct your own exception handling semantics, but the Gauche system won’t save if something goes wrong. Use these primitives when you want to customize the system’s higher-level semantics or you are porting from other SRFI-18 code.
[SRFI-18] Returns the current exception handler.
A procedure handler must take one argument. This procedure
sets handler to the current exception handler and calls
(Note that this slightly differs from R7RS
we’ll explain it below.)
When an exception is raised by
handler is called with the thrown condition in the
exactly same dynamic environment of
It means the exception handler is also the same,
raise in handler reinvokes handler again.
It also means that when handler returns, it returns from
The behavior is specified in SRFI-18, intending this procedure to be the most primitive building block of exception handling mechanism. If you need to switch handlers, you can do it by yourself.
If you need the typical semantics where raising exception in
the exception handler is handled by outer handler, you should use
Use this procedure only when you want to play with the most
primitive layer of exception handling.
R7RS has a procedure with the same name, but has one difference—it
changes the active exception handler to the “outer” handler before
calling handler. See R7RS base library, for the description
If an exception is raised by
error, or the thrown condition
<serious-condition>, it is prohibited to return from
handler. If handler ever returns in such cases, another
error is signaled, with replacing the current exception handler to the
outer handler. So the caller of
error, or the caller of
<serious-condition>, can assume it never returns.
The behavior of those procedures can be explained in the following conceptual Scheme code.
;; Conceptual implementation of low-level exception mechanism. ;; Suppose %xh is a list of exception handlers (define (current-exception-handler) (car %xh)) (define (raise exn) (receive r ((car %xh) exn) (when (uncontinuable-exception? exn) (set! %xh (cdr %xh)) (raise (make-error "returned from uncontinuable exception"))) (apply values r))) (define (with-exception-handler handler thunk) (let ((prev %xh)) (dynamic-wind (lambda () (set! %xh (cons handler %xh))) thunk (lambda () (set! %xh prev)))))
Gauche currently has the following hierarchy of built-in condition classes. It approximately reflects SRFI-35 and SRFI-36 condition hierarchy, although they have Gauche-style class names. If there’s a corresponding SRFI condition type, the class has the SRFI name as well.
<condition> +- <compound-condition> +- <serious-condition> | +- <serious-compound-condition> ; also inherits <compound-condition> +- <message-condition> +- <error> ; also inherits <serious-condition> +- <system-error> +- <unhandled-signal-error> +- <read-error> +- <io-error> +- <port-error> +- <io-read-error> +- <io-write-error> +- <io-closed-error> +- <io-unit-error>
Note that some conditions may occur simultaneously; for example,
error during reading from a file because of device failure may consist
In such cases, a compound condition is raised.
So you can’t just use, for instance,
(is-a? obj <io-read-error>)
to check if
<io-read-error> is thrown.
See the "Condition API" section below.
Every condition class is an instance of this class.
This class defines
object-apply so that you can use
a condition class as a predicate, e.g.:
(<error> obj) ≡ (condition-has-type? obj <error>)
[SRFI-35] The root class of the condition hierarchy.
Represents a compound condition. A compound condition can be
created from one or more conditions by
Don’t use this class directly.
A compound condition returns
if any of the original conditions has the given type.
Conditions of this class are for the situations that are too serious
to ignore or continue. Particularly, you can safely assume that
raise this type of condition, it never returns.
This is an internal class to represent a compound condition
with any of its component condition is serious. Inherits both
make-compound-condition uses this class if the passed
conditions includes a serious one.
Don’t use this class directly.
[SRFI-35] This class represents a condition with a message. It has one slot.
Indicates an error. Inherits
<message-condition>, thus has
&error condition only inherits
&message, so you have to use compound condition
to attach a message to the error condition. Gauche uses multiple
inheritance here, largely because of backward compatibility.
To write a portable code, an error condition should be used
with a message condition, like this:
(condition (&message (message "Error message")) (&error))
A subclass of
When a system call returns an error, this type of exception is
message slot usually contains the description
of the error (like the one from
Besides that, this class has one more instance slot:
Contains an integer value of system’s error number.
Error numbers may differ among systems. Gauche defines
constants for typical Unix error values (e.g.
etc), so it is desirable to use them instead of literal numbers.
See the description of
sys-strerror in System inquiry
for available constants.
This class doesn’t have corresponding SRFI condition type,
but important to obtain OS’s raw error code. In some cases,
this type of condition is compounded with other condition
A subclass of
<error>. The default handler of most of
signals raises this condition. See Handling signals for
An integer indicating the received signal number. There are constants defined for typical signal numbers; see Signals and signal sets.
A subclass of
When the reader detects a lexical or syntactic error during
reading an S-expression, this type of condition is raised.
A port from which the reader is reading.
&read-error doesn’t have this slot. Portable
program shouldn’t rely on this slot).
A line count (1-base) of the input where the reader raised this error. It may be -1 if the reader is reading from a port that doesn’t keep track of line count.
These slots are defined in SRFI-36’s
For the time being, these slots always hold
A base class of I/O errors. Inherits
An I/O error related to a port. Inherits
Holds the port where the error occurred.
An I/O error during reading from a port. Inherits
An I/O error during writing to a port. Inherits
An I/O error when read/write is attempted on a closed port.
An I/O error when the read/write is requested with a unit
that is not supported by the port (e.g. a binary I/O is requested
on a character-only port). Inherits
Defines a new condition type. In Gauche, a condition type is
a class, whose metaclass is
Name becomes the name of the new type, and also the variable
of that name is bound to the created condition type.
Supertype is the name of the supertype (direct superclass)
of this condition type. A condition type must inherit from
<condition> or its descendants.
(Multiple inheritance can’t be specified by this form, and generally
should be avoided in condition type hierarchy. Instead, you
can use compound conditions, which don’t introduce multiple inheritance.)
A variable predicate is bound to a predicate procedure for this condition type.
field-spec is a form of
and the condition will have fields named by field-name, and
a variable accessor-name will be bound to a procedure that
accesses the field. In Gauche, each field becomes a slot of
the created class.
Gauche extends srfi-35 to allow predicate and/or
accessor-name to be omitted,
if you don’t need to them to be defined.
define-condition-type is expanded into a class
definition, each slot gets a
:init-keyword slot option
with the keyword whose name is the same as the slot name.
#t iff obj is a condition type. In Gauche,
(is-a? obj <condition-meta>).
[SRFI-35] A procedural version to create a new condition type.
Creates a new condition of condition-type type, and
initializes its fields as specified by
#t iff obj is a condition. In Gauche,
(is-a? obj <condition>).
#t iff obj belongs to a condition type type.
Because of compound conditions, this is not equivalent to
Retrieves the value of field field-name of condition.
If condition is a compound condition, you can access to the
field of its original conditions; if more than one original condition
have field-name, the first one passed to
You can use slot-ref and/or ref to access to the field
of conditions; compound conditions define a
so that slot-ref behaves as if the compound conditions have all the
slots of the original conditions. Using
Returns a compound condition that has all condition0 condition1
…. The returned condition’s fields are the union of all the fields
of given conditions; if any conditions have the same name of fields,
the first one takes precedence. The returned condition also has
condition-type of all the types of given conditions.
(This is not a multiple inheritance. See
[SRFI-35] Condition must be a condition and have type condition-type. This procedure returns a condition of condition-type, with field values extracted from condition.
A convenience macro to create a (possibly compound) condition.
Type-field-binding is a form of
(condition-type (field-name value-expr) …).
(condition (type0 (field00 value00) ...) (type1 (field10 value10) ...) ...) ≡ (make-compound-condition (make-condition type0 'field00 value00 ...) (make-condition type1 'field10 value10 ...) ...)