[Editorial Note by KMP: This syntax stuff is still very confused and needs lots of work.]
define-condition
name ({parent-type}*)
({!slot-spec}*)
{option}*
⇒ name
slot-spec | ::= | slot-name | (slot-name !slot-option) |
slot-option | ::= | [[ {:reader symbol}* | {:writer !function-name}* | {:accessor symbol}* | {:allocation !allocation-type} | {:initarg symbol}* | {:initform form} | {:type type-specifier} ]] |
option | ::= | [[ (:default-initargs . initarg-list) |
(:documentation string) |
(:report report-name) ]]
|
function-name | ::= | {symbol | (setf symbol) }
|
allocation-type | ::= | :instance | :class
|
report-name | ::= | string | symbol | lambda expression |
name | a symbol. |
parent-type | a symbol naming a condition type.
If no parent-types are supplied,
the parent-types default to (condition) .
|
default-initargs | a list of keyword/value pairs.
[Editorial Note by KMP: This is all mixed up as to which is a slot option and which is a main option. I’ll sort that out. Also, some of this is implied by the bnf and needn’t be stated explicitly.] |
Slot-spec | the name of a slot or a list consisting of the slot-name followed by zero or more slot-options. |
Slot-name | a slot name (a symbol), the list of a slot name, or the list of slot name/slot form pairs. |
Option | Any of the following:
|
define-condition defines a new condition type called name, which is a subtype of
the type or types named by parent-type. Each parent-type argument specifies a direct supertype of the new condition. The new condition inherits slots and methods from each of its direct supertypes, and so on.
If a slot name/slot form pair is supplied, the slot form is a form that can be evaluated by make-condition to produce a default value when an explicit value is not provided. If no slot form is supplied, the contents of the slot is initialized in an implementation-dependent way.
If the type being defined and some other type from which it inherits have a slot by the same name, only one slot is allocated in the condition, but the supplied slot form overrides any slot form that might otherwise have been inherited from a parent-type. If no slot form is supplied, the inherited slot form (if any) is still visible.
Accessors are created according to the same rules as used by defclass.
A description of slot-options follows:
:reader
The :reader
slot option specifies that an unqualified method is
to be defined on the generic function named by the argument
to :reader
to read the value of the given slot.
The :initform
slot option is used to provide a default
initial value form to be used in the initialization of the slot. This
form is evaluated every time it is used to initialize the
slot. The
lexical environment
in which this form is evaluated is the lexical
environment in which the define-condition
form was evaluated.
Note that the lexical environment refers both to variables and to
functions.
For local slots, the dynamic environment is the dynamic
environment
in which make-condition was called; for
shared slots, the dynamic environment
is the dynamic environment in which the
define-condition form was evaluated.
[Reviewer Note by Barmar: Issue CLOS-CONDITIONS doesn’t say this.]
No implementation is permitted to extend the syntax of define-condition
to allow (slot-name form)
as an abbreviation for
(slot-name :initform form)
.
:initarg
The :initarg
slot option declares an initialization
argument named by its symbol argument
and specifies that this
initialization argument initializes the given slot. If the
initialization argument has a value in the call to
initialize-instance, the value is stored into the given slot,
and the slot’s :initform
slot option, if any, is not
evaluated. If none of the initialization arguments specified for a
given slot has a value, the slot is initialized according to the
:initform
slot option, if specified.
:type
The :type
slot option specifies that the contents of the
slot is always of the specified type. It effectively
declares the result type of the reader generic function when applied
to an object of this condition type.
The consequences of attempting to store in a
slot a value that
does not satisfy the type of the slot is undefined.
:default-initargs
[Editorial Note by KMP: This is an option, not a slot option.]
This option is treated the same as it would be defclass.
:documentation
[Editorial Note by KMP: This is both an option and a slot option.]
The :documentation
slot option provides a documentation string
for the slot.
:report
[Editorial Note by KMP: This is an option, not a slot option.]
Condition reporting is mediated through the print-object
method for the condition type in question, with *print-escape*
always being nil. Specifying (:report report-name)
in the definition of a condition type C
is equivalent to:
(defmethod print-object ((x c) stream)
(if *print-escape* (call-next-method) (report-name x stream)))
If the value supplied by the argument to :report
(report-name)
is a symbol or a lambda expression,
it must be acceptable to
function. (function report-name)
is evaluated
in the current lexical environment.
It should return 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 report-name is a string, it is a shorthand for
(lambda (condition stream)
(declare (ignore condition))
(write-string report-name stream))
This 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 supplied, information about how to report this
type of condition is inherited from the parent-type.
The consequences are unspecifed if an attempt is made to read a slot that has not been explicitly initialized and that has not been given a default value.
The consequences are unspecified if an attempt is made to assign the slots by using setf.
If a define-condition form appears as a top level form, the compiler must make name recognizable as a valid type name, and it must be possible to reference the condition type as the parent-type of another condition type in a subsequent define-condition form in the file being compiled.
The following form defines a condition of type
peg/hole-mismatch
which inherits from a condition type
called blocks-world-error
:
(define-condition peg/hole-mismatch
(blocks-world-error)
((peg-shape :initarg :peg-shape
:reader peg/hole-mismatch-peg-shape)
(hole-shape :initarg :hole-shape
:reader peg/hole-mismatch-hole-shape))
(:report (lambda (condition stream)
(format stream "A ~A peg cannot go in a ~A hole."
(peg/hole-mismatch-peg-shape condition)
(peg/hole-mismatch-hole-shape condition)))))
The new type has slots peg-shape
and hole-shape
,
so make-condition accepts :peg-shape
and :hole-shape
keywords.
The readers peg/hole-mismatch-peg-shape
and peg/hole-mismatch-hole-shape
apply to objects of this type, as illustrated in the :report
information.
The following form defines a condition type named machine-error
which inherits from error:
(define-condition machine-error
(error)
((machine-name :initarg :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, a new error condition can be defined which
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)))))
This defines a still more specific condition, built upon
machine-not-available-error
, which provides a slot initialization form
for machine-name
but which 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
is used to
report this type of condition.
(define-condition ate-too-much (error)
((person :initarg :person :reader ate-too-much-person)
(weight :initarg :weight :reader ate-too-much-weight)
(kind-of-food :initarg :kind-of-food
:reader :ate-too-much-kind-of-food)))
⇒ ATE-TOO-MUCH
(define-condition ate-too-much-ice-cream (ate-too-much)
((kind-of-food :initform 'ice-cream)
(flavor :initarg :flavor
:reader ate-too-much-ice-cream-flavor
:initform 'vanilla ))
(:report (lambda (condition stream)
(format stream "~A ate too much ~A ice-cream"
(ate-too-much-person condition)
(ate-too-much-ice-cream-flavor condition)))))
⇒ ATE-TOO-MUCH-ICE-CREAM
(make-condition 'ate-too-much-ice-cream
:person 'fred
:weight 300
:flavor 'chocolate)
⇒ #<ATE-TOO-MUCH-ICE-CREAM 32236101>
(format t "~A" *)
|> FRED ate too much CHOCOLATE ice-cream
⇒ NIL