Common Lisp the Language, 2nd Edition

Next: Exhaustive Case Analysis Up: Program Interface to Previous: Signaling Conditions

## 29.4.2. Assertions

These facilities are designed to make it convenient for the user to insert error checks into code.

[Macro]
`check-type place typespec [string]`

[This supersedes the description of `check-type` given in section 24.2.-GLS]

A `check-type` form signals an error of type `type-error` if the contents of place are not of the desired type.

If a condition is signaled, handlers of this condition can use the functions `type-error-datum` and `type-error-expected-type` to access the contents of place and the typespec, respectively.

This function can return only if the `store-value` restart is invoked, either explicitly from a handler or implicitly as one of the options offered by the debugger. The restart is associated with the signaled condition as if by use of `with-condition-restarts`.

If `store-value` is called, `check-type` will store the new value that is the argument to `store-value` (or that is prompted for interactively by the debugger) in place and start over, checking the type of the new value and signaling another error if it is still not the desired type. Subforms of place may be evaluated multiple times because of the implicit loop generated. `check-type` returns `nil`.

The place must be a generalized variable reference acceptable to `setf`. The typespec must be a type specifier; it is not evaluated. The `string` should be an English description of the type, starting with an indefinite article (``a'' or ``an''); it is evaluated. If the string is not supplied, it is computed automatically from the typespec. (The optional string argument is allowed because some applications of `check-type` may require a more specific description of what is wanted than can be generated automatically from the type specifier.)

The error message will mention the place, its contents, and the desired type.

Implementation note: An implementation may choose to generate a somewhat differently worded error message if it recognizes that place is of a particular form, such as one of the arguments to the function that called `check-type`.

    Lisp> (setq aardvarks '(sam harry fred))
     => (SAM HARRY FRED)
    Lisp> (check-type aardvarks (array * (3)))
    Error: The value of AARDVARKS, (SAM HARRY FRED),
           is not a 3-long array.
    To continue, type :CONTINUE followed by an option number:
     1: Specify a value to use instead.
     2: Return to Lisp Toplevel.
    Debug> :continue 1
    Use Value: #(sam fred harry)
     => NIL
    Lisp> aardvarks
     => #<ARRAY-3 13571>
    Lisp> (map 'list #'identity aardvarks)
     => (SAM FRED HARRY)
    Lisp> (setq aacount 'foo)
     => FOO
    Lisp> (check-type aacount (integer 0 *) "a non-negative integer")
    Error: The value of AACOUNT, FOO, is not a non-negative integer.
    To continue, type :CONTINUE followed by an option number:
     1: Specify a value to use instead.
     2: Return to Lisp Toplevel.
    Debug> :continue 2
    Lisp>

Compatibility note: In Zetalisp, the equivalent facility is called `check-arg-type`.

[Macro]
`assert test-form [({place}*) [datum {argument}*]]`

[This supersedes the description of `assert` given in section 24.2.-GLS]

An `assert` form signals an error if the value of the test-form is `nil`. Continuing from this error using the `continue` restart will allow the user to alter the values of some variables, and `assert` will then start over, evaluating the test-form again. (The restart is associated with the signaled condition as if by use of `with-condition-restarts`.) `assert` returns `nil`.

The test-form may be any form. Each place (there may be any number of them, or none) must be a generalized variable reference acceptable to `setf`. These should be variables on which test-form depends, whose values may sensibly be changed by the user in attempting to correct the error. Subforms of each place are evaluated only if an error is signaled, and may be re-evaluated if the error is re-signaled (after continuing without actually fixing the problem).

The datum and arguments are evaluated only if an error is to be signaled, and re-evaluated if the error is to be signaled again.

If datum is a condition, then that condition is used directly. In this case, it is an error to specify any arguments.

If datum is a condition type (a class or class name), then the condition used is effectively the result of `(apply #'make-condition datum (list argument))`.

If datum is a string, then the condition used is effectively the result of

    (make-condition 'simple-error
                    :format-string datum
                    :format-arguments (list argument))

If datum is omitted, then a condition of type `simple-error` is constructed using the test-form as data. For example, the following might be used:

    (make-condition 'simple-error
      :format-string "The assertion ~S failed."
      :format-arguments '(test-form))

Note that the test-form itself, and not its value, is used as the format argument.

Implementation note: The debugger need not include the test-form in the error message, and any places should not be included in the message, but they should be made available for the user's perusal. If the user gives the ``continue'' command, an opportunity should be presented to alter the values of any or all of the references. The details of this depend on the implementation's style of user interface, of course.

Here is an example of the use of `assert`:

    (setq x (make-array '(3 5) :initial-element 3))
    (setq y (make-array '(3 5) :initial-element 7))

    (defun matrix-multiply (a b)
      (let ((*print-array* nil))
        (assert (and (= (array-rank a) (array-rank b) 2)
                     (= (array-dimension a 1)
                        (array-dimension b 0)))
                (a b)
                "Cannot multiply ~S by ~S." a b)
        (really-matrix-multiply a b)))

    (matrix-multiply x y)
    Error: Cannot multiply #<ARRAY-3-5 12345> by #<ARRAY-3-5 12364>.
    To continue, type :CONTINUE followed by an option number:
     1: Specify new values.
     2: Return to Lisp Toplevel.
    Debug> :continue 1
    Value for A: x
    Value for B: (make-array '(5 3) :initial-element 6)
     => #2A(¯(54 54 54 54 54)
    (54 54 54 54 54)
    		(54 54 54 54 54)
    		(54 54 54 54 54)
    		(54 54 54 54 54))

Next: Exhaustive Case Analysis Up: Program Interface to Previous: Signaling Conditions