Common Lisp the Language, 2nd Edition


next up previous contents index
Next: Defining Conditions Up: Program Interface to Previous: Exhaustive Case Analysis

29.4.4. Handling Conditions

change_begin
These macros allow a program to gain control when a condition is signaled.


[Macro]
handler-case expression {(typespec ([var]) {form}*)}*

Executes the given expression in a context where various specified handlers are active.

Each typespec may be any type specifier. If during the execution of the expression a condition is signaled for which there is an appropriate clause-that is, one for which (typep condition 'typespec) is true-and if there is no intervening handler for conditions of that type, then control is transferred to the body of the relevant clause (unwinding the dynamic state appropriately in the process) and the given variable var is bound to the condition that was signaled. If no such condition is signaled and the computation runs to completion, then the values resulting from the expression are returned by the handler-case form.

If more than one case is provided, those cases are made accessible in parallel. That is, in

(handler-case expression 
  (type1 (var1) form1) 
  (type2 (var2) form2))

if the first clause (containing form1) has been selected, the handler for the second is no longer visible (and vice versa).

The cases are searched sequentially from top to bottom. If a signaled condition matches more than one case (possible if there is type overlap) the earlier of the two cases will be selected.

If the variable var is not needed, it may be omitted. That is, a clause such as

(type (var) (declare (ignore var)) form)

may be written using the following shorthand notation:

(type () form)

If there are no forms in a selected case, the case returns nil. Note that

(handler-case expression 
  (type1 (var1) . body1) 
  (type2 (var2) . body2) 
  ...)

is approximately equivalent to

(block #1=#:block-1 
  (let (#2=#:var-2) 
    (tagbody 
      (handler-bind ((type1 ¯#'(lambda (temp) 
		    (setq #2# temp) 
		    (go #3=#:tag-3)))
                     (type2 ¯#'(lambda (temp) 
		    (setq #2# temp) 
		    (go #4=#:tag-4)))
                     ...) 
        (return-from #1# expression)) 
      #3# (return-from #1# (let ((var1 #2#)) . body1)) 
      #4# (return-from #1# (let ((var2 #2#)) . body2)) 
      ...)))

[Note the use of ``gensyms'' such as #:block-1 as block names, variables, and tagbody tags in this example, and the use of #n= and #n# read-macro syntax to indicate that the very same gensym appears in multiple places.-GLS]

As a special case, the typespec can also be the symbol :no-error in the last clause. If it is, it designates a clause that will take control if the expression returns normally. In that case, a completely general lambda-list may follow the symbol :no-error, and the arguments to which the lambda-list parameters are bound are like those for multiple-value-call on the return value of the expression. For example,

(handler-case expression 
  (type1 (var1) . body1) 
  (type2 (var2) . body2) 
  ... 
  (typen (varn) . bodyn) 
  (:no-error (nvar1 nvar2 ... nvarm) . nbody))

is approximately equivalent to

(block #1=#:error-return 
  (multiple-value-call #'(lambda (nvar1 nvar2 ... nvarm) . nbody) 
    (block #2=#:normal-return 
      (return-from #1# 
        (handler-case (return-from #2# expression) 
          (type1 (var1) . body1) 
          (type2 (var2) . body2) 
          ... 
          (typen (varn) . bodyn))))))

Examples of the use of handler-case:

(handler-case (/ x y) 
  (division-by-zero () nil)) 

(handler-case (open *the-file* :direction :input) 
  (file-error (condition) (format t "~&Fooey: ~A~%" condition))) 

(handler-case (some-user-function) 
  (file-error (condition) condition) 
  (division-by-zero () 0) 
  ((or unbound-variable undefined-function) () 'unbound)) 

(handler-case (intern x y) 
  (error (condition) condition) 
  (:no-error (symbol status) 
    (declare (ignore symbol)) 
    status))


[Macro]
ignore-errors {form}*

Executes its body in a context that handles conditions of type error by returning control to this form. If no such condition is signaled, any values returned by the last form are returned by ignore-errors. Otherwise, two values are returned: nil and the error condition that was signaled.

ignore-errors could be defined by

(defmacro ignore-errors (&body forms) 
  `(handler-case (progn ,@forms) 
     (error (c) (values nil c)))


[Macro]
handler-bind ({(typespec handler)}*) {form}*

Executes body in a dynamic context where the given handler bindings are in effect. Each typespec may be any type specifier. Each handler form should evaluate to a function to be used to handle conditions of the given type(s) during execution of the forms. This function should take a single argument, the condition being signaled.

If more than one binding is specified, the bindings are searched sequentially from top to bottom in search of a match (by visual analogy with typecase). If an appropriate typespec is found, the associated handler is run in a context where none of the handler bindings are visible (to avoid recursive errors). For example, in the case of

(handler-bind ((unbound-variable #'(lambda ...)) 
               (error #'(lambda ...))) 
  ...)

if an unbound variable error is signaled in the body (and not handled by an intervening handler), the first function will be called. If any other kind of error is signaled, the second function will be called. In either case, neither handler will be active while executing the code in the associated function.
change_end



next up previous contents index
Next: Defining Conditions Up: Program Interface to Previous: Exhaustive Case Analysis


AI.Repository@cs.cmu.edu