Common Lisp the Language, 2nd Edition
Next: Creating Conditions
Up: Program Interface to
Previous: Handling
Conditions
[The contents of this section are still a subject of some debate within
X3J13. The reader may wish to take this section with a grain of salt,
two aspirin tablets, and call a hacker in the morning.-GLS]
[Macro]
define-condition name ({parent-type}*)
[({slot-specifier}*) {option}*]
Defines a new condition type called name, which is a subtype of each given parent-type. Except as otherwise noted, the arguments are not evaluated.
Objects of this condition type will have all of the indicated slots, plus any additional slots inherited from the parent types (its superclasses). If the slots list is omitted, the empty list is assumed.
A slot must have the form
slot-specifier ::= slot-name | (slot-name [[?slot-option]])
For the syntax of a slot-option, see defclass
.
The slots of a condition object are normal CLOS slots. Note that
with-slots
may be used instead of accessor functions to
access slots of a condition object.
make-condition
will accept keywords (in the keyword
package) with the print name of any of the designated slots, and will
initialize the corresponding slots in conditions it creates.
Accessors are created according to the same rules as used by
defclass
.
The valid options are as follows:
(:documentation
doc-string
)
The doc-string should be either nil
or a string
that describes the purpose of the condition type. If this option is
omitted, nil
is assumed. Calling
(documentation '
name
'type)
will retrieve this information.
(:report
exp
)
If exp is not a literal string, it must be a suitable argument
to the function
special form. The expression
(function
exp
)
will be
evaluated in the current lexical environment. It should produce a
function of two arguments, a condition and a stream, that prints on the
stream a description of the condition. This function is called whenever
the condition is printed while *print-escape*
is
nil
.
If exp is a literal string, it is shorthand for
(lambda (c s)
(declare (ignore c))
(write-string exp s))
[That is, a function is provided that will simply write the given string literally to the stream, regardless of the particular condition object supplied.-GLS]
The :report
option is processed after the new
condition type has been defined, so use of the slot accessors within the
report function is permitted. If this option is not specified,
information about how to report this type of condition will be inherited
from the parent-type.
[X3J13 voted in March 1989 (ZLOS-CONDITIONS) to integrate the
Condition System and the Object System. In the original Condition System
proposal, define-condition
allowed only one
parent-type (the inheritance structure was a simple hierarchy).
Slot descriptions were much simpler, even simpler than those for
defstruct
:
slot ::= slot-name | (slot-name) | (slot-name default-value)
Similarly, define-condition
allowed a
:conc-name
option similar to that of
defstruct
:
(:conc-name
symbol-or-string
)
Not now part of Common Lisp. As with
defstruct
, this sets up automatic prefixing of the names of
slot accessors. Also as in defstruct
, the default behavior
is to use the name of the new type, name, followed by a hyphen.
(Generated names are interned in the package that is current at the time
that the define-condition
is processed).
One consequence of the vote was to make define-condition
slot descriptions like those of defclass
.-GLS]
Here are some examples of the use of
define-condition
.
The following form defines a condition of type
peg/hole-mismatch
that inherits from a condition type
called blocks-world-error
:
(define-condition peg/hole-mismatch (blocks-world-error)
(peg-shape hole-shape)
(:report
(lambda (condition stream)
(with-slots (peg-shape hole-shape) condition
(format stream "A ~A peg cannot go in a ~A hole."
peg-shape hole-shape))))
The new type has slots peg-shape
and
hole-shape
, so make-condition
will accept
:peg-shape
and :hole-shape
keywords. The
with-slots
macro may be used to access the
peg-shape
and hole-shape
slots, as illustrated
in the :report
information.
Here is another example. This defines a condition called
machine-error
that inherits from error
:
(define-condition machine-error (error)
((machine-name
:reader machine-error-machine-name))
(:report (lambda (condition stream)
(format stream "There is a problem with ~A."
(machine-error-machine-name condition)))))
Building on this definition, we can define a new error condition that
is a subtype of machine-error
for use when machines are not
available:
(define-condition machine-not-available-error (machine-error) ()
(:report (lambda (condition stream)
(format stream "The machine ~A is not available."
(machine-error-machine-name condition)))))
We may now define a still more specific condition, built upon
machine-not-available-error
, that provides a default for
machine-name
but does not provide any new slots or report
information. It just gives the machine-name
slot a default
initialization:
(define-condition my-favorite-machine-not-available-error
(machine-not-available-error)
((machine-name :initform "MC.LCS.MIT.EDU")))
Note that since no :report
clause was given, the
information inherited from machine-not-available-error
will
be used to report this type of condition.
Next: Creating Conditions
Up: Program Interface to
Previous: Handling
Conditions
AI.Repository@cs.cmu.edu