by Peter McGoron
This SRFI is currently in draft status. Here is an explanation of each status that a SRFI can hold. To provide input on this SRFI, please send email to srfi-288@nospamsrfi.schemers.org. To subscribe to the list, follow these instructions. You can access previous messages via the mailing list archive.
A condition object system is introduced that attempts to be implementable in and interoperable with many implementation’s condition systems, including the condition system of the R6RS. The condition system includes compound conditions, but does not include subtyping of conditions. Simple conditions can be any implementation-defined value, and are also objects that can be made with the interface in this SRFI. An implementation may add other features, such as introspection and serialization.
(srfi 288 warning) for &warning,
but this is very tedious. Maybe these should be in an
implementation-specified library.This section is non-normative.
Conditions, which are objects that describe exceptional
situations, are implemented in many Scheme systems. In the R7RS, error objects are provided
as a way to signal errors with accompanying information, and two special
predicates (read-error? and file-error?)
are provided to check if an exception was raised by certain
procedures. These operations are designed such that they can
interoperate with the native condition system of the implementation.
The R6RS introduced a system of condition types where conditions were record types: condition types were inspectable could be subtyped. The record system of the R6RS has been criticized elsewhere [Clinger 2007, 2009], and those arguments are not particularly relevant here. The important thing is, the R6RS condition system requires the use of the criticized R6RS record type system.
The fragmentation of condition objects across implementations is an
impediment to portable code. Portable code is forced to use the
error procedure, which makes exceptional situations
difficult to describe and recover from. There is no standard
type in R7RS used to
report non-error conditions, leaving programmers to use ad-hoc
record objects.
The system described in this SRFI is an attempt to find a reduced subset of the behavior of various implementations. The goals are:
One consequence is that the SRFI does not provide for the subtyping of condition types: this is harder to make interoperate with other Scheme condition systems. Many of the benefits of subtyping can be obtained by using compound conditions.
Although the SRFI does not provide subtyping or introspection features, that does not mean that an implementation cannot provide them. Indeed, this entire SRFI can be implemented portably on top of the R6RS condition type system. Implementations are encouraged to add their own features to the condition system defined in this SRFI, which may include single inheritance, multiple inheritance, introspection, serialization, or generic functions.
Why not add subtyping instead of compound conditions? Subtyping is a more popular feature than compound conditions: see Appendix 1: Other systems. However, requiring subtyping and using it as the only method of extension makes it difficult to have a portable set of standard conditions, because different implementations have different condition hierarchies. There are cases where the condition hierarchy is not possible to change, for example when a Scheme implementation is embedded in another system that offers exceptions. Example include, Kawa (Java exceptions) and LIPS (Javascript exceptions).
This SRFI could have added both subtyping and compound conditions. It does not because subtyping might not be a part of some record type systems in some implementations. Also, without a procedural or inspection interface, there is not much benefit to subtyping versus compound conditions.
The basic procedural interface described in this SRFI is not a required feature because R7RS records do not have a procedural interface. This SRFI is designed around the fact that an implementation’s condition is usually closely tied to its record/object system, and some of those systems only have syntactic interfaces.
A more expansive procedural/inspection interface is not provided because implementations differ in behavior when they employ extensions to this SRFI, and it is difficult to write a specification that is forwards compatible with such extensions. For example,
For example, R6RS’s
record-accessor will accept a number index for a field
that starts at the first extended field, and does not allow for
creating accessors for fields of supertypes without the supertype RTD.
Meanwhile, MIT-Scheme allows for accessing the fields of a supertype
condition.
A procedural interface allows for constructing condition objects based off of runtime values, as opposed to only at expansion time.
The R6RS has a procedure
named simple-conditions, that returns the simple conditions
of a compound condition in order. This procedure is useful when two
conditions in a compound condition have the same field accessor: this
allows the user to get both values of the field.
This feature is not required in this SRFI because it was missing from SRFI 12 and SRFI 35. Conditions of the same type are shadowed in these systems, and the implementation is free to discard them, although at least the sample implementation does not do this.
It is useful to coalesce fields with the same name, because then the condition type can be represented in a multiple-inheritance object system. However, if this is not required, then an implementation should provide this interface. In particular, implementations that have native exceptions, for example Java exceptions, should provide this interface because it allows the user to access objects which may have operations that are outside of this SRFI (for instance, mutability and object identity).
The (srfi 288) and (srfi 288 base) library
is required. The other libraries are optional but should be supplied.
If two identifiers in separate libraries have the same name, and the implementation provides both libraries, then those identifiers must be equivalent: i.e. one is a re-export of the other.
The (srfi 288) library exports all identifiers available
to the implementation.
(srfi 288 base)
syntax(define-condition-type ⟨type name⟩ (⟨constructor⟩ ⟨field⟩ …) ⟨predicate⟩ (⟨fielda⟩ ⟨accessor⟩) …)
It is an error if any syntactic variable is not an identifier.
Creates a new condition type (generatively). The ⟨type name⟩ is bound to some implementation-defined value (which is possibly not a runtime value).
The ⟨constructor⟩ is bound to a procedure of as many arguments as there are ⟨field⟩s and returns a condition of the specified type.
The ⟨predicate⟩ returns #t if the
passed object was either created with the ⟨constructor⟩, or is a compound
condition containing a condition that would make ⟨predicate⟩ return
#t if passed to it, or and returns #f otherwise.
Each ⟨accessor⟩ will access the value passed to the constructor
in the slot corresponding to ⟨fielda⟩ as in
define-record-type. If an ⟨accessor⟩ is given a compound
condition with an instance of this condition inside of it, the ⟨accessor⟩
will return the value from the first instance of the condition.
(condition?
obj)
Returns #t if obj is a simple or compound
condition, and #f otherwise.
(condition
condition1
condition2 …)
If only one condition is supplied and it is simple, return the condition unchanged.
Otherwise, return a compound condition that contains each of the conditions in order. If one of the conditions in the argument list is compound, then the simple conditions inside of the compound condition are spliced into the list.
(make-condition-type
symbol
fields)
It is an error if fields is not either a list or a vector, and it is an error if the elements are not symbols that are all unique.
Create a new condition type with the name symbol. This procedure is generative: each invocation, even with the same symbol and field names, return a disjoint condition type.
Four values are returned:
An implementation specific value that represents the condition type. For example, in R6RS, this should be the record type descriptor for the condition type.
A constructor that take as many arguments as there are fields, and returns a simple condition with those arguments as the fields of the condition. The returned condition is immutable.
A predicate that returns true if the object is either a simple condition created with the constructor, or if the object is a compound condition that contains an object that returns true when passed to the predicate.
A vector with the same length as fields, which are accessors for this condition.
(simple-conditions
condition)
If condition is simple, return a list with only condition as an element.
If condition is compound, return a list that contains the simple conditions of condition in order.
The implementation may coalesce condition types when creating a compound
condition: if two simple condition
objects are eqv? or are both simple immutable condition
objects of the same type that have fields that are eqv? and
are not separated by condition objects that share the same accessors,
then those two condition objects may be combined into one condition
object.
For example, consider simple condition objects c1 and c2 that both have no fields and are of the same type. Then these condition objects are immutable, and can be coalesced into one condition type inside of the compound condition, regardless of where they are.
Unless specified, these conditions may be simple or compound. As such,
the implementation dependent values that would correspond to
&message, &irritants, etc. are not provided.
An implementation that makes these conditions simple should provide these
identifiers.
On R6RS implementations, identifiers with the same name as identifiers defined in the R6RS are re-exports of those identifiers.
In this section, it is an error if this-condition is not a condition that satisfies the predicate in the same block.
A condition that signals a warning.
(make-message-condition string)(condition-message this-condition)(message-condition? obj)A condition that contains a message in prose that describes the condition.
R7RS error objects are message conditions. This does not mean that the error objects are compound conditions or are defined through any mechanism specified in this SRFI.
(make-irritants-condition list)(condition-irritants this-condition)(irritants-condition? obj)A condition that contains a list of associated values. Usually, this is the arguments passed to some procedure.
R7RS error objects are irritants conditions. This does not mean that they error objects are compound conditions or are defined through any mechanism specified in this SRFI.
A “serious” condition, signalled when something has gone wrong or requires immediate intervention.
An R7RS error object, file error object, or read error object is also a serious condition.
A condition that signals an error caused by something that is usually external to the program, such as filesystem errors or errors from malformed input. As a rule of thumb, if a procedure is designed to be called on untrusted input or an external resource, then if it encounters a situation where it cannot continue normally, it will raise an exception of this type.
An R7RS file error object or read error object is also an external error condition.
Objects of this condition type are also serious conditions.
R6RS &error
conditions are external error conditions.
Rationale: This was renamed from the R6RS name “error” because “error” in common use generally encompasses what R6RS refers to as “assertion violations.” This name makes it more explicit what type of errors this condition type describes.
An assertion violation is raised when a procedure is called incorrectly. For example, when a procedure is passed the wrong number of arguments, or if a procedure is given a value of the wrong type.
Objects of this contrition type are also serious conditions.
Note: A Scheme program should* never depend on assertion violations being raised. Locations where assertion violations occur are generally those locations where the behavior “is an error” in the R7RS. They are places where extensions may occur, either in newer standards or in implementations.
In many Scheme systems, the “error” condition or exception type really corresponds to an assertion violation.
*: Used in the sense of the authors opinion, not an RFC 2119 requirement word.
(error-object? obj)(read-error? obj)(file-error? obj)(error-object-message error-object)(error-object-irritants error-object)These procedures are extended such that:
error-object? if it is a compound condition
object that has a system error object inside of it.read-error? if it is a compound condition
object with a system read-error object inside of it.file-error? if it is a compound condition
object with a system file-error object inside of it.error-object-message
and error-object-irritants
works on compound conditions with error objects in them.
An implementation may make an error object a compound condition that is made up of a serious condition, message condition, and irritants condition.
Note: Error objects, read errors, and file errors are not required to be compound condition objects. An implementation can continue to use its old representations.
Implementations are encouraged to replace the versions of these
procedures in (scheme base) with the one defined in this SRFI. The
ones defined in this SRFI are backwards-compatible with the versions
described in the R7RS.
This section is non-normative.
(define-condition-type &type-mismatch
(make-type-mismatch value expected)
type-mismatch?
(value type-mismatch:value)
(expected type-mismatch:expected))
(define-condition-type &who
(make-who-condition who)
who-condition?
(who condition-who))
(define (safe-car pair)
(unless (pair? pair)
(raise (condition (make-type-mismatch pair "pair")
(make-assertion-violation)
(make-who-condition 'car))))
(car pair))
(define cc (guard (x (else x)) (safe-car 5)))
(error? cc) ; ⇒ #f
(assertion-violation? cc) ; ⇒ #t
(who-condition? cc) ; ⇒ #t
(type-mismatch? cc) ; ⇒ #t
(condition-who cc) ; ⇒ car
(type-mismatch:value cc) ; ⇒ 5
(type-mismatch:expected cc) ; ⇒ "pair"
Implementations of this SRFI for various Scheme implementations are provided in the SRFI repository.
If an implementation has a set of disjoint types meant to be used as conditions, then the following implementation strategy should be followed:
#t when applied to a
compound condition with an object of that type in it.This may entail a change to the representation of error objects in the implementation, and any exported predicates/accessors will have to be changed. However, code that was previously written for the implementation’s error object system will be compatible with this SRFI’s system, and allow for complete interoperation with condition types defined using the interface in this SRFI.
This strategy is only transparent when the conditions in question are considered as abstract objects. If enough of the implementation details of the conditions are accessible, then it is impossible to transparently replace the conditions with this system, as it would be with any other system.
An implementation for MIT-Scheme is provided as a patch to the
runtime and a library file implementing the identifiers of this SRFI.
Compound conditions are instances of a MIT-Scheme condition
type that has single field. The modifications to the runtime mean
that procedures constructed using condition-accessor
and condition-predicate will automatically work with
compound conditions.
MIT-Scheme is an instructive example of how one could extend the condition system. MIT-Scheme conditions have a continuation and restarter field. Compound conditions take the continuation and restarter field of the first condition passed to the constructor.
This SRFI is a subset of the behavior of the R6RS
system.
Condition types defined by this SRFI are inheritable
by R6RS’s
define-condition-type and inspectable using
the R6RS’s
interfaces, and conditions defined by the R6RS can be used in the procedures
defined in this SRFI.
An implementation that wraps SRFI 35’s API is available. The conditions defined in this SRFI are completely interoperable with SRFI 35 conditions. The implementation assumes that the implementation uses SRFI 35 for R7RS error objects.
An implementation that wraps CHICKEN’s extended SRFI 12 implementation is provided. Conditions defined by this SRFI are CHICKEN 6 conditions, and so are compound conditions created by this SRFI. Hence, the two systems are completely interoperable. CHICKEN’s standard exceptions are categorized into external errors or assertion violations by the sample implementation.
This implementation makes every object a condition object. SRFI 222 compound objects are compound condition objects. No procedural interface is provided.
This implementation should only be used if the implementation has no native condition/exception object system.
I thank William Clinger, R. Kent Dybvig, Matthew Flatt, Marc Feeley (SRFI 12 authors), Richard Kelsey, and Michael Sperber (SRFI 35 authors), for their work defining compound condition systems in Scheme.
This table covers the various non-R6RS implementations that were studied in the process of writing this SRFI.
| Implementation | Condition system (beyond R7RS) |
|---|---|
| Bigloo | No compound conditions, conditions are defined using the object system |
| Biwa | None (does not implement error) |
| Chibi Scheme | Error objects have a kind field |
| CHICKEN 6 | SRFI 12, expanded |
| Cyclone | None (error objects are pairs) |
| Gauche | SRFI 35, modified condition hierarchy, multiple inheritance |
| Gambit | None (exceptions are records, there is no condition? supertype) |
| Gerbil | Exception objects are defined with multiple-inheritance object system |
| Goldfish | None (derived from s7) |
| Kawa | Java exceptions |
| LIPS | Javascript exceptions |
| Meevax | None |
| MIT-Scheme | Single inheritance condition system without compound conditions |
| s7 | None |
| Foment | Error objects have who and kind fields |
| STKlos | SRFI 35 |
| SKINT | None? |
| Stak | None |
Notably, Racket uses an exception system without compound conditions, only single inheritance.
© 2026 Peter McGoron.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS,” WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
The sample implementation includes a patch to MIT-Scheme. That file may be redistributed and/or modified under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The patch is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. See the GNU General Public License for more details.