516 lines
21 KiB
HTML
516 lines
21 KiB
HTML
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
|
|
<html><head><title>SRFI 35: Conditions</title></head><body>
|
|
<h1>Title</h1>
|
|
|
|
Conditions
|
|
|
|
<h1>Authors</h1>
|
|
|
|
Richard Kelsey and Michael Sperber
|
|
|
|
<h1>Status</h1>
|
|
|
|
This SRFI is currently in ``final'' status. To see an explanation of
|
|
each status that a SRFI can hold, see <a href="http://srfi.schemers.org/srfi-process.html">here</a>. It will
|
|
remain in draft until 2002-10-20, or as amended. to provide input on
|
|
this SRFI, please mail to
|
|
<a href="mailto:srfi%20minus%2035%20at%20srfi%20dot%20schemers%20dot%20org">
|
|
<code>srfi minus 35 at srfi dot schemers dot org</code></a>.
|
|
See <a href="http://srfi.schemers.org/srfi-list-subscribe.html">
|
|
instructions here</a> to subscribe to the list. You can access
|
|
the discussion via
|
|
<a href="http://srfi.schemers.org/srfi-35/mail-archive/maillist.html">
|
|
the archive of the mailing list</a>.
|
|
You can access
|
|
post-finalization messages via
|
|
<a href="http://srfi.schemers.org/srfi-34/post-mail-archive/maillist.html">
|
|
the archive of the mailing list</a>.
|
|
|
|
<ul>
|
|
<li>Draft: 2002/07/24-2002/10/20</li>
|
|
<li>Revised: 2002/09/20</li>
|
|
<li>Final: 2002/12/1</li>
|
|
</ul>
|
|
|
|
<h1>Abstract</h1>
|
|
<p>The SRFI defines constructs for creating and inspecting <i>condition</i>
|
|
types and values. A condition value encapsulates information about an
|
|
exceptional situation, or exception. This SRFI also defines a few basic
|
|
condition types.</p>
|
|
<h1>Rationale</h1>
|
|
<p>Conditions are values that communicate information about exceptional
|
|
situations between parts of a program. Code that detects an exception
|
|
may be in a different part of the program than the code that handles
|
|
it. In fact, the former may have been written independently from the
|
|
latter. Consequently, to facilitate effective handling of exceptions,
|
|
conditions must communicate as much information as possible as
|
|
accurately as possible, and still allow effective handling by code that
|
|
did not precisely anticipate the nature of the exception that occurred.</p>
|
|
<p>This SRFI provides two mechanisms to enable this kind of communication</p><ul><li>subtyping among condition types allows handling code to determine the <em>general</em>
|
|
nature of an exception even though it does not anticipate its <em>exact</em>
|
|
nature,</li>
|
|
<li>compound conditions allow an exceptional situation to be described in multiple ways.</li>
|
|
</ul>
|
|
<p></p>
|
|
<h1>Specification</h1>
|
|
<p>Conditions are records with named fields. Each condition belongs to one or more <i>condition types</i>.
|
|
Each condition type specifies a set of field names. A condition
|
|
belonging to a condition type includes a value for each of the type's
|
|
field names. These values can be extracted from the condition by using
|
|
the appropriate field name.</p>
|
|
<p>There is a tree of condition types with the distinguished <code>&condition</code>
|
|
as its root. All other condition types have a parent condition type.</p>
|
|
<p>A condition belonging to several condition types with a common
|
|
supertype may have distinct values for the supertype's fields for each
|
|
type. The type used to access a field determines which of the values is
|
|
returned. The program can extract each of these field values separately.</p>
|
|
<h2>Procedures</h2>
|
|
<dl><dt><code>(make-condition-type </code><var>id</var>
|
|
<var>parent</var>
|
|
<var>field-names</var><code>)</code></dt>
|
|
<dd><p><code>Make-condition-type</code>
|
|
returns a new condition type. <var>Id</var>
|
|
must be a symbol that serves as a symbolic name for the condition type. <var>Parent</var>
|
|
must itself be a condition type. <var>Field-names</var>
|
|
must be a list of symbols. It identifies the fields of the conditions associated with the condition type.</p>
|
|
<p><var>Field-names</var>
|
|
must be disjoint from the field names of <var>parent</var>
|
|
and its ancestors.</p>
|
|
</dd>
|
|
<dt><code>(condition-type? </code><var>thing</var><code>)</code></dt>
|
|
<dd><p><code>Condition-type?</code>
|
|
is a predicate for condition types: it returns <code>#t</code>
|
|
if <var>thing</var>
|
|
is a condition type, and <code>#f</code>
|
|
otherwise</p>
|
|
</dd>
|
|
<dt><code>(make-condition </code><var>type</var>
|
|
<var>field-name</var>
|
|
<var>value</var>
|
|
...<code>)</code></dt>
|
|
<dd><p><code>Make-condition</code>
|
|
creates a condition value belonging condition type <var>type</var>. The following arguments must be, in turn, a field name and an arbitrary value. There must be such a pair for each field of <var>type</var>
|
|
and its direct and indirect supertypes. <code>Make-condition</code>
|
|
returns the condition value, with the argument values associated with their respective fields.</p>
|
|
</dd>
|
|
<dt><code>(condition? </code><var>thing</var><code>)</code></dt>
|
|
<dd><p><code>Condition?</code>
|
|
is a predicate for conditions: it returns <code>#t</code>
|
|
if <var>thing</var>
|
|
is a condition type, and <code>#f</code>
|
|
otherwise</p>
|
|
</dd>
|
|
<dt><code>(condition-has-type? </code><var>condition</var>
|
|
<var>condition-type</var><code>)</code></dt>
|
|
<dd><p><code>Condition-has-type?</code>
|
|
tests if condition <var>condition</var>
|
|
belongs to condition type <var>condition-type</var>. It returns <code>#t</code>
|
|
if any of <var>condition</var>
|
|
's types includes <var>condition-type</var>
|
|
eitherdirectlyorasanancestorand
|
|
<code>#f</code>
|
|
otherwise.</p>
|
|
<p>It is an error if <var>condition</var>
|
|
is not a condition, or if <var>condition-type</var>
|
|
is not a condition type.</p>
|
|
</dd>
|
|
<dt><code>(condition-ref </code><var>condition</var>
|
|
<var>field-name</var><code>)</code></dt>
|
|
<dd><p><var>Condition</var>
|
|
must be a condition, and <var>field-name</var>
|
|
a symbol. Moreover, <var>condition</var>
|
|
must belong to a condition type which has a field name called <var>field-name</var>, or one of its (direct or indirect) supertypes must have the field. <code>Condition-ref</code>
|
|
returns the value associated with <var>field-name</var>.</p>
|
|
<p>It is an error to refer to a field the condition does not have.</p>
|
|
</dd>
|
|
<dt><code>(make-compound-condition </code><var>condition<sub>0</sub>
|
|
</var>
|
|
<var>condition<sub>1</sub>
|
|
...</var><code>)</code></dt>
|
|
<dd><p><code>Make-compound-condition</code>
|
|
returns a compound condition belonging to all condition types that the <var>condition<sub>i</sub>
|
|
</var>
|
|
belong to. </p>
|
|
<p><code>Condition-ref</code>, when applied to a compound condition will return the value from the first of the <var>condition<sub>i</sub>
|
|
</var>
|
|
that has such a field.</p>
|
|
</dd>
|
|
<dt><code>(extract-condition </code><var>condition</var>
|
|
<var>condition-type</var><code>)</code></dt>
|
|
<dd><p><var>Condition</var>
|
|
must be a condition belonging to <var>condition-type</var>. <code>Extract-condition</code>
|
|
returns a condition of condition type <var>condition-type</var>
|
|
with the field values specified by <var>condition</var>.</p>
|
|
<p>If <var>condition</var>
|
|
is a compound condition, <code>extract-condition</code>
|
|
extracts the field values from the subcondition belonging to <var>condition-type</var>
|
|
that appeared first in the call to <code>make-compound-condition</code>
|
|
that created the condition. The returned condition may be newly created; it is possible for</p><pre>(let* ((&c (make-condition-type 'c &condition '()))
|
|
(c0 (make-condition &c))
|
|
(c1 (make-compound-condition c0)))
|
|
(eq? c0 (extract-condition c1 &c)))
|
|
</pre>to return false.<p></p>
|
|
</dd>
|
|
</dl>
|
|
<h2>Macros</h2>
|
|
<dl><dt><code>(define-condition-type </code><condition-type> <supertype> <predicate> <field-spec> ...<code>)</code></dt>
|
|
<dd><p>This defines a new condition type. <Condition-type>, <supertypes>, and <predicate> must all be identifiers. <code>Define-condition-type</code>
|
|
defines an identifier <condition-type> to some value describing
|
|
the condition type. <supertype> must be the name of a previously
|
|
defined condition type. </p>
|
|
<p><code>Define-condition-type</code> also defines <predicate> to
|
|
a predicate that identifies conditions associated with that type, or
|
|
with any of its subtypes.</p>
|
|
<p>Each <field-spec> must be of the form <code>(</code>
|
|
<field> <accessor><code>)</code>
|
|
where both <field> and <accessor> must be identifiers. <code>Define-condition-type</code>
|
|
defines each <accessor> to a procedure which extracts the value
|
|
of the named field from a condition associated with this condition type.</p>
|
|
</dd>
|
|
<dt><code>(condition </code><type-field-binding> ...<code>)</code></dt>
|
|
<dd><p>This creates a condition value. Each <type-field-binding> must be of the form <code>(</code>
|
|
<condition-type> <field-binding> ...<code>)</code>
|
|
Each <field-binding> must be of the form <code>(</code>
|
|
<field> <exp><code>)</code>
|
|
where <field> is a field identifier from the definition of <condition-type>. </p>
|
|
<p>The <exp> are evaluated in some unspecified order; their
|
|
values can later be extracted from the condition object via the
|
|
accessors of the associated condition types or their supertypes.</p>
|
|
<p>The condition returned by <code>condition</code>
|
|
is created by a call of form</p>
|
|
<pre>(make-compound-condition
|
|
(make-condition <condition-type> '<field-name> <value>...)
|
|
...)</pre>
|
|
<p> with the condition types retaining their order from the<code>condition</code>
|
|
form. The field names and values are duplicated as necessary as described below.</p>
|
|
<p>Each <type-field-binding> must contain field bindings for <em>all</em>
|
|
fields of <condition-type> without duplicates. There is an
|
|
exception to this rule: if a field binding is missing, and the field
|
|
belongs to a supertype shared with one of the other
|
|
<type-field-binding> subforms, then the value defaults to that of
|
|
the first such binding in the <code>condition</code>
|
|
form.</p>
|
|
</dd>
|
|
</dl>
|
|
<h2>Standard Conditions</h2>
|
|
<dl><dt><code>&condition</code>
|
|
</dt>
|
|
<dd><p>This is the root of the entire condition type hierarchy. It has a no fields.</p>
|
|
</dd>
|
|
<dt><code>&message</code>
|
|
</dt>
|
|
<dd><p>This condition type could be defined by</p><pre>(define-condition-type &message &condition
|
|
message-condition?
|
|
(message condition-message))
|
|
</pre><p></p>
|
|
<p>It carries a message further describing the nature of the condition to humans.</p>
|
|
</dd>
|
|
<dt><code>&serious</code>
|
|
</dt>
|
|
<dd><p>This condition type could be defined by</p><pre>(define-condition-type &serious &condition
|
|
serious-condition?)
|
|
</pre><p></p>
|
|
<p>This type describes conditions serious enough that they cannot
|
|
safely be ignored. This condition type is primarily intended as a
|
|
supertype of other condition types.</p>
|
|
</dd><dt><code>&error</code>
|
|
</dt>
|
|
<dd><p>This condition type could be defined by</p><pre>(define-condition-type &error &serious
|
|
error?)
|
|
</pre><p></p>
|
|
<p>This condition describes errors, typically caused by something that
|
|
has gone wrong in the interaction of the program with the external
|
|
world or the user.</p>
|
|
</dd>
|
|
|
|
</dl>
|
|
<h1>Examples</h1>
|
|
<pre>(define-condition-type &c &condition
|
|
c?
|
|
(x c-x))
|
|
|
|
(define-condition-type &c1 &c
|
|
c1?
|
|
(a c1-a))
|
|
|
|
(define-condition-type &c2 &c
|
|
c2?
|
|
(b c2-b))
|
|
(define v1 (make-condition &c1 'x "V1" 'a "a1"))
|
|
|
|
(c? v1) => #t
|
|
(c1? v1) => #t
|
|
(c2? v1) => #f
|
|
(c-x v1) => "V1"
|
|
(c1-a v1) => "a1"
|
|
|
|
(define v2 (condition (&c2
|
|
(x "V2")
|
|
(b "b2"))))
|
|
|
|
(c? v2) => #t
|
|
(c1? v2) => #f
|
|
(c2? v2) => #t
|
|
(c-x v2) => "V2"
|
|
(c2-b v2) => "b2"
|
|
|
|
(define v3 (condition (&c1
|
|
(x "V3/1")
|
|
(a "a3"))
|
|
(&c2
|
|
(b "b3"))))
|
|
|
|
(c? v3) => #t
|
|
(c1? v3) => #t
|
|
(c2? v3) => #t
|
|
(c-x v3) => "V3/1"
|
|
(c1-a v3) => "a3"
|
|
(c2-b v3) => "b3"
|
|
|
|
(define v4 (make-compound-condition v1 v2))
|
|
|
|
(c? v4) => #t
|
|
(c1? v4) => #t
|
|
(c2? v4) => #t
|
|
(c-x v4) => "V1"
|
|
(c1-a v4) => "a1"
|
|
(c2-b v4) => "b2"
|
|
|
|
(define v5 (make-compound-condition v2 v3))
|
|
|
|
(c? v5) => #t
|
|
(c1? v5) => #t
|
|
(c2? v5) => #t
|
|
(c-x v5) => "V2"
|
|
(c1-a v5) => "a3"
|
|
(c2-b v5) => "b2"
|
|
</pre><h1>Reference Implementation</h1>
|
|
<p>The reference implementation makes use of <a href="http://srfi.schemers.org/srfi-1/">SRFI 1</a>
|
|
("List Library"), <a href="http://srfi.schemers.org/srfi-9/">SRFI 9</a>
|
|
("Defining Record Types"), and <a href="http://srfi.schemers.org/srfi-23/">SRFI 23</a>
|
|
("Error reporting mechanism").</p>
|
|
<pre>(define-record-type :condition-type
|
|
(really-make-condition-type name supertype fields all-fields)
|
|
condition-type?
|
|
(name condition-type-name)
|
|
(supertype condition-type-supertype)
|
|
(fields condition-type-fields)
|
|
(all-fields condition-type-all-fields))
|
|
|
|
(define (make-condition-type name supertype fields)
|
|
(if (not (symbol? name))
|
|
(error "make-condition-type: name is not a symbol"
|
|
name))
|
|
(if (not (condition-type? supertype))
|
|
(error "make-condition-type: supertype is not a condition type"
|
|
supertype))
|
|
(if (not
|
|
(null? (lset-intersection eq?
|
|
(condition-type-all-fields supertype)
|
|
fields)))
|
|
(error "duplicate field name" ))
|
|
(really-make-condition-type name
|
|
supertype
|
|
fields
|
|
(append (condition-type-all-fields supertype)
|
|
fields)))
|
|
|
|
(define-syntax define-condition-type
|
|
(syntax-rules ()
|
|
((define-condition-type ?name ?supertype ?predicate
|
|
(?field1 ?accessor1) ...)
|
|
(begin
|
|
(define ?name
|
|
(make-condition-type '?name
|
|
?supertype
|
|
'(?field1 ...)))
|
|
(define (?predicate thing)
|
|
(and (condition? thing)
|
|
(condition-has-type? thing ?name)))
|
|
(define (?accessor1 condition)
|
|
(condition-ref (extract-condition condition ?name)
|
|
'?field1))
|
|
...))))
|
|
|
|
(define (condition-subtype? subtype supertype)
|
|
(let recur ((subtype subtype))
|
|
(cond ((not subtype) #f)
|
|
((eq? subtype supertype) #t)
|
|
(else
|
|
(recur (condition-type-supertype subtype))))))
|
|
|
|
(define (condition-type-field-supertype condition-type field)
|
|
(let loop ((condition-type condition-type))
|
|
(cond ((not condition-type) #f)
|
|
((memq field (condition-type-fields condition-type))
|
|
condition-type)
|
|
(else
|
|
(loop (condition-type-supertype condition-type))))))
|
|
|
|
; The type-field-alist is of the form
|
|
; ((<type> (<field-name> . <value>) ...) ...)
|
|
(define-record-type :condition
|
|
(really-make-condition type-field-alist)
|
|
condition?
|
|
(type-field-alist condition-type-field-alist))
|
|
|
|
(define (make-condition type . field-plist)
|
|
(let ((alist (let label ((plist field-plist))
|
|
(if (null? plist)
|
|
'()
|
|
(cons (cons (car plist)
|
|
(cadr plist))
|
|
(label (cddr plist)))))))
|
|
(if (not (lset= eq?
|
|
(condition-type-all-fields type)
|
|
(map car alist)))
|
|
(error "condition fields don't match condition type"))
|
|
(really-make-condition (list (cons type alist)))))
|
|
|
|
(define (condition-has-type? condition type)
|
|
(any (lambda (has-type)
|
|
(condition-subtype? has-type type))
|
|
(condition-types condition)))
|
|
|
|
(define (condition-ref condition field)
|
|
(type-field-alist-ref (condition-type-field-alist condition)
|
|
field))
|
|
|
|
(define (type-field-alist-ref type-field-alist field)
|
|
(let loop ((type-field-alist type-field-alist))
|
|
(cond ((null? type-field-alist)
|
|
(error "type-field-alist-ref: field not found"
|
|
type-field-alist field))
|
|
((assq field (cdr (car type-field-alist)))
|
|
=> cdr)
|
|
(else
|
|
(loop (cdr type-field-alist))))))
|
|
|
|
(define (make-compound-condition condition-1 . conditions)
|
|
(really-make-condition
|
|
(apply append (map condition-type-field-alist
|
|
(cons condition-1 conditions)))))
|
|
|
|
(define (extract-condition condition type)
|
|
(let ((entry (find (lambda (entry)
|
|
(condition-subtype? (car entry) type))
|
|
(condition-type-field-alist condition))))
|
|
(if (not entry)
|
|
(error "extract-condition: invalid condition type"
|
|
condition type))
|
|
(really-make-condition
|
|
(list (cons type
|
|
(map (lambda (field)
|
|
(assq field (cdr entry)))
|
|
(condition-type-all-fields type)))))))
|
|
|
|
(define-syntax condition
|
|
(syntax-rules ()
|
|
((condition (?type1 (?field1 ?value1) ...) ...)
|
|
(type-field-alist->condition
|
|
(list
|
|
(cons ?type1
|
|
(list (cons '?field1 ?value1) ...))
|
|
...)))))
|
|
|
|
(define (type-field-alist->condition type-field-alist)
|
|
(really-make-condition
|
|
(map (lambda (entry)
|
|
(cons (car entry)
|
|
(map (lambda (field)
|
|
(or (assq field (cdr entry))
|
|
(cons field
|
|
(type-field-alist-ref type-field-alist field))))
|
|
(condition-type-all-fields (car entry)))))
|
|
type-field-alist)))
|
|
|
|
(define (condition-types condition)
|
|
(map car (condition-type-field-alist condition)))
|
|
|
|
(define (check-condition-type-field-alist the-type-field-alist)
|
|
(let loop ((type-field-alist the-type-field-alist))
|
|
(if (not (null? type-field-alist))
|
|
(let* ((entry (car type-field-alist))
|
|
(type (car entry))
|
|
(field-alist (cdr entry))
|
|
(fields (map car field-alist))
|
|
(all-fields (condition-type-all-fields type)))
|
|
(for-each (lambda (missing-field)
|
|
(let ((supertype
|
|
(condition-type-field-supertype type missing-field)))
|
|
(if (not
|
|
(any (lambda (entry)
|
|
(let ((type (car entry)))
|
|
(condition-subtype? type supertype)))
|
|
the-type-field-alist))
|
|
(error "missing field in condition construction"
|
|
type
|
|
missing-field))))
|
|
(lset-difference eq? all-fields fields))
|
|
(loop (cdr type-field-alist))))))
|
|
|
|
(define &condition (really-make-condition-type '&condition
|
|
#f
|
|
'()
|
|
'()))
|
|
|
|
(define-condition-type &message &condition
|
|
message-condition?
|
|
(message condition-message))
|
|
|
|
(define-condition-type &serious &condition
|
|
serious-condition?)
|
|
|
|
(define-condition-type &error &serious
|
|
error?)
|
|
</pre><h1>References</h1>
|
|
<ul><li><a href="http://srfi.schemers.org/srfi-12/">SRFI 12: Exception Handling</a>
|
|
by William Clinger, R. Kent Dybvig, Matthew Flatt, and Marc Feeley</li>
|
|
<li><a href="http://www.swiss.ai.mit.edu/ftpdir/scheme-mail/HTML/rrrs-1996/msg00022.html">Richard Kelsey's 1996 proposal</a>
|
|
</li>
|
|
<li><a href="http://world.std.com/%7Epitman/Papers/Condition-Handling-2001.html">Kent Pitman's history paper</a>
|
|
</li>
|
|
<li>The <a href="http://www.xanalys.com/software_tools/reference/HyperSpec/Body/09_.htm">Conditions chapter</a>
|
|
from the <a href="http://www.xanalys.com/software_tools/reference/HyperSpec/Front/index.htm">Common Lisp HyperSpec</a>
|
|
</li>
|
|
<li>The Conditions chapter by Kent M. Pitman in <a href="http://www-2.cs.cmu.edu/afs/cs.cmu.edu/project/ai-repository/ai/html/cltl/cltl2.html"><i>Common Lisp the Language, 2nd edition</i>
|
|
</a>
|
|
by Guy L. Steele</li>
|
|
<li>The <a href="http://www.gwydiondylan.org/drm/drm_52.htm#HEADING52-0">Conditions chapter</a>
|
|
in the <a href="http://www.gwydiondylan.org/drm/drm_1.htm">Dylan Reference Manual</a>
|
|
</li>
|
|
</ul>
|
|
|
|
<h1>Copyright</h1>
|
|
|
|
<p>Copyright (C) Richard Kelsey, Michael Sperber (2002). All Rights
|
|
Reserved.</p>
|
|
|
|
<p>
|
|
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:
|
|
</p>
|
|
<p>
|
|
The above copyright notice and this permission notice shall be
|
|
included in all copies or substantial portions of the Software.
|
|
</p>
|
|
<p>
|
|
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.
|
|
</p>
|
|
|
|
<hr>
|
|
<address>Editor: <a href="mailto:srfi%20minus%20editors%20at%20srfi%20dot%20schemers%20dot%20org">Francisco Solsona</a></address>
|
|
|
|
</body></html>
|