Common Lisp the Language, 2nd Edition

next up previous contents index
Next: Creating Conditions Up: Program Interface to Previous: Handling Conditions

29.4.5. Defining 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]


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) 
    (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) 
                    :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-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 up previous contents index
Next: Creating Conditions Up: Program Interface to Previous: Handling Conditions