Next: , Previous: , Up: restarts   [Index]


1.18.4 Handling signalled conditions

Syntax: handler-bind (?clause …) ?body0 ?body

Evaluate body forms in a dynamic environment in which new exception handlers are installed; it is capable of handling exceptions raised with raise, raise-continuable and signal. Not quite like R6RS’s with-exception-handler syntax, but similar.

The arguments ?clause must have the following syntax:

(?typespec ?condition-handler)

Every ?typespec is meant to be a logic predicate with the format:

?typespec = (?tag)
          | (and ?inner-pred0 ?inner-pred ...)
          | (or  ?inner-pred0 ?inner-pred ...)
          | (xor ?inner-pred0 ?inner-pred ...)
          | (not ?inner-pred)

?inner-pred = ?tag
            | (and ?inner-pred0 ?inner-pred ...)
            | (or  ?inner-pred0 ?inner-pred ...)
            | (xor ?inner-pred0 ?inner-pred ...)
            | (not ?inner-pred)

where: each ?tag is an identifier usable as second argument to is-a?; and, or, xor, not are the identifiers exported by (vicare).

Every ?condition-handler must be an expression evaluating to a procedure accepting a condition object as single argument; the condition object can be simple or compound.

If the body performs a normal return: the values returned by the body become the values returned by handler-bind.

If an exception is raised (in Common Lisp jargon: a condition is signalled): a condition handler matching the raised object is searched in the sequence of clauses, left–to–right:

The handlers are called with a continuation whose dynamic environment is that of the call to raise, raise-continuable or signal that raised the exception; except that the current exception handler is the one that was in place when handler-bind was evaluated.

When a condition handler is applied to the raised condition object:

Usage examples on handling raised conditions:

(import (vicare)
  (only (vicare checks)
        with-result
        add-result))

;;no condition
(with-result
  (handler-bind
      ((&error   (lambda (E)
                   (add-result 'error-handler)
                   1))
       (&warning (lambda (E)
                   (add-result 'warning-handler)
                   2)))
    (add-result 'body)
    1))
⇒ (1 (body))

;;;

;;Escaping from handler, which is the normal way of accepting
;;to handle a condition.
;;
(with-result
  (call/cc
      (lambda (escape)
        (handler-bind
            ((&error (lambda (E)
                       (add-result 'error-handler)
                       (escape 2))))
          (add-result 'body-begin)
          (signal (make-error))
          (add-result 'body-return)
          1))))
⇒ (2 (body-begin error-handler))

;;The first handler declines to handle a raised exception,
;;the second one accepts.
;;
(with-result
  (call/cc
      (lambda (escape)
        (handler-bind
            ((&warning (lambda (E)
                         ;;By returning this handler declines.
                         (add-result 'warning-handler)))
             (&error   (lambda (E)
                         (add-result 'error-handler)
                         (escape 2))))
          (add-result 'body-begin)
          (raise (condition (make-error)
                            (make-warning)))
          (add-result 'body-return)
          1))))
⇒ (2 (body-begin warning-handler error-handler))

;;Multiple condition identifiers in the same clause.
;;
(with-result
  (call/cc
      (lambda (escape)
        (handler-bind
            (((&warning &error) (lambda (E)
                                  (add-result 'handler)
                                  (escape 2))))
          (add-result 'body-begin)
          (signal (make-error))
          (add-result 'body-return)
          1))))
⇒ (2 (body-begin handler))

;;Nested HANDLER-BIND uses, returning from handler.
;;
(with-result
  (call/cc
      (lambda (escape)
        (handler-bind
            ((&error (lambda (E)
                       (add-result 'outer-error-handler)
                       (escape 2))))
          (handler-bind
              ((&error (lambda (E)
                         (add-result 'inner-error-handler))))
            (add-result 'body-begin)
            (raise (make-error))
            (add-result 'body-return)
            1)))))
⇒ (2 (body-begin inner-error-handler outer-error-handler))

;;; unwind-protect

(internal-body

  (define (doit C)
    (with-result
      (returnable
        (handler-bind
            ((&error   (lambda (E)
                         (add-result 'error-handler)
                         (return 1))))
          (with-unwind-handler
              (lambda (why)
                (add-result 'outer-unwind-handler))
            (lambda ()
              (handler-bind
                  ((&warning (lambda (E)
                               (add-result 'warning-handler)
                               (return 2))))
                (with-unwind-handler
                    (lambda (why)
                      (add-result 'inner-unwind-handler))
                  (lambda ()
                    (add-result 'body-begin)
                    (signal C)
                    (add-result 'body-normal-return))))))))))

  (doit (make-error))
  ⇒ (1
        (body-begin
         error-handler
         inner-unwind-handler
         outer-unwind-handler))

  (doit (make-warning))
  ⇒ (2
        (body-begin
         warning-handler
         inner-unwind-handler
         outer-unwind-handler))

  #| end of body |# )

Usage examples on invoking restarts:

(import (vicare)
  (only (vicare checks)
        with-result
        add-result))

;;Nested RESTART-CASE and HANDLER-BIND.  Signal a condition,
;;call a handler, invoke a restart.
;;
(internal-body

  (define (restarts-outside/handlers-inside C)
    (with-result
      (restart-case
          (handler-bind
              ((&error   (lambda (E)
                           (add-result 'error-handler-begin)
                           (invoke-restart 'alpha)
                           (add-result 'error-handler-return)))
               (&warning (lambda (E)
                           (add-result 'warning-handler-begin)
                           (invoke-restart 'beta)
                           (add-result 'warning-handler-return))))
            (begin
              (add-result 'body-begin)
              (signal C)
              (add-result 'body-return)))
        (alpha (lambda ()
                 (add-result 'restart-alpha)
                 1))
        (beta  (lambda ()
                 (add-result 'restart-beta)
                 2)))))

  (restarts-outside/handlers-inside (make-error))
  ⇒ (1 (body-begin error-handler-begin restart-alpha))

  (restarts-outside/handlers-inside (make-warning))
  ⇒ (2 (body-begin warning-handler-begin restart-beta))

  #| end of body |# )

;;Nested RESTART-CASE and HANDLER-BIND.  Signal a condition,
;;call a handler, invoke a restart.
;;
(internal-body

  (define (restarts-inside/handlers-outside C)
    (with-result
      (handler-bind
          ((&error   (lambda (E)
                       (add-result 'error-handler-begin)
                       (invoke-restart 'alpha)
                       (add-result 'error-handler-return)))
           (&warning (lambda (E)
                       (add-result 'warning-handler-begin)
                       (invoke-restart 'beta)
                       (add-result 'warning-handler-return))))
        (restart-case
            (begin
              (add-result 'body-begin)
              (signal C)
              (add-result 'body-return))
          (alpha (lambda ()
                   (add-result 'restart-alpha)
                   1))
          (beta  (lambda ()
                   (add-result 'restart-beta)
                   2))))))

  (restarts-inside/handlers-outside (make-error))
  ⇒ (1 (body-begin error-handler-begin restart-alpha))

  (restarts-inside/handlers-outside (make-warning))
  ⇒ (2 (body-begin warning-handler-begin restart-beta))

  #| end of body |# )

;;Nested RESTART-CASE and HANDLER-BIND.  Signal a condition,
;;call a handler, invoke a restart.
;;
(internal-body

  (define (restarts-inside/nested-handlers C)
    (with-result
      (handler-bind
          ((&error   (lambda (E)
                       (add-result 'error-handler-begin)
                       (invoke-restart 'alpha)
                       (add-result 'error-handler-return))))
        (handler-bind
            ((&warning (lambda (E)
                         (add-result 'warning-handler-begin)
                         (invoke-restart 'beta)
                         (add-result 'warning-handler-return))))
          (restart-case
              (begin
                (add-result 'body-begin)
                (signal C)
                (add-result 'body-return))
            (alpha (lambda ()
                     (add-result 'restart-alpha)
                     1))
            (beta  (lambda ()
                     (add-result 'restart-beta)
                     2)))))))

  (restarts-inside/nested-handlers (make-error))
  ⇒ (1 (body-begin error-handler-begin restart-alpha))

  (restarts-inside/nested-handlers (make-warning))
  ⇒ (2 (body-begin warning-handler-begin restart-beta))

  #| end of LET |# )

;;Nested RESTART-CASE and HANDLER-BIND.  Signal a condition,
;;call a handler, invoke a restart.
;;
(internal-body

  (define (nested-restarts/handlers-outside C)
    (with-result
      (handler-bind
          ((&error   (lambda (E)
                       (add-result 'error-handler)
                       (invoke-restart 'alpha)))
           (&warning (lambda (E)
                       (add-result 'warning-handler)
                       (cond ((find-restart 'beta)
                              => (lambda (handler)
                                   (invoke-restart handler)))))))
        (restart-case
            (restart-case
                (begin
                  (add-result 'body-begin)
                  (signal C)
                  (add-result 'body-return))
              (alpha (lambda ()
                       (add-result 'restart-alpha)
                       1)))
          (beta  (lambda ()
                   (add-result 'restart-beta)
                   2))))))

  (nested-restarts/handlers-outside (make-error))
  ⇒ (1 (body-begin error-handler restart-alpha))

  (nested-restarts/handlers-outside (make-warning))
  ⇒ (2 (body-begin warning-handler restart-beta))

  #| end of body |# )

;;Nested RESTART-CASE and HANDLER-BIND.  Signal a condition,
;;call the first handler, the first handler declines, call
;;the second handler, invoke a restart.
;;
(with-result
  (handler-bind
      ((&message (lambda (E)
                   (add-result 'outer-message-handler-begin)
                   (invoke-restart 'alpha E)
                   (add-result 'outer-message-handler-return))))
    (handler-bind
        ((&message (lambda (E)
                     (add-result 'inner-message-handler))))
      (restart-case
          (begin
            (add-result 'body-begin)
            (signal (make-message-condition "ciao"))
            (add-result 'body-return))
        (alpha (lambda (E)
                 (add-result 'alpha-restart)
                 (condition-message E)))))))
⇒ ("ciao"
      (body-begin
       inner-message-handler
       outer-message-handler-begin
       alpha-restart))

Next: , Previous: , Up: restarts   [Index]