Next: , Previous: , Up: iklib unwind-protect   [Index]


6.9.4 Raising non–continuable exceptions from the thunk

A non–continuable exception is raised with raise, which can be applied to any object; the argument to raise becomes the “raised object”. The purpose of raising a non–continuable exception is to cause the application of a function, the current exception handler, to the raised object; the exception handler is called in the dynamic environment of the call to raise.

The execution flow is not meant return to the caller of raise, so the exception handler has only two options:

  1. Reinstate a previously saved continuation to jump out of the dynamic environment in which the exception was raised. The following example shows the basic mechanism:
    (import (vicare))
    
    (call/cc
        (lambda (escape)
          (with-exception-handler
              escape
            (lambda ()
              (raise 1)))))
    ⇒ 1
    

    Reinstating a continuation to jump out of the exception handler is what the standard guard syntax does when it handles an exception; guard does more than this.

  2. Raise the exception again, causing the invocation of the upper level exception handler. The following example shows the basic mechanism:
    (import (vicare))
    
    (call/cc
        (lambda (escape)
          (with-exception-handler
              escape
            (lambda ()
              (with-exception-handler
                  (lambda (E)
                    (raise E))
                (lambda ()
                  (raise 1)))))))
    ⇒ 1
    

Handling exceptions by escaping

This is not the correct way of interfacing with the unwind–protection mechanism. In the following example we see that escaping from the exception handler skips the call to the ?unwind-handler.

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

(with-result
  (call/cc
      (lambda (escape)
        (with-exception-handler
            (lambda (E)
              (add-result 'exception-handler)
              (escape E))
          (lambda ()
            (with-unwind-protection
                (lambda (why)
                  (add-result 'cleanup))
              (lambda ()
                (add-result 'thunk-in)
                (raise 123)
                (add-result 'thunk-out))))))))
⇒ (123 (thunk-in exception-handler))

Handling exceptions with guard

This is the correct way of interfacing with the unwind–protection mechanism.

In the following example: the ?thunk raises a non–continuable exception, which is catched by the else clause of a guard use; this is equivalent to escaping from an exception handler, but it does evaluate the ?unwind-handler. We can see the forms evaluation order.

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

(with-result
  (guard (E (else
             (add-result 'guard-else)
             E))
    (with-unwind-protection
        (lambda (why)
          (add-result 'cleanup))
      (lambda ()
        (add-result 'thunk-in)
        (raise 2)
        (add-result 'thunk-out)
        1))))
⇒ (2 (thunk-in cleanup guard-else))

In the following example: the ?thunk raises a non–continuable exception, which is catched by the clause of a guard use; the clause has test and expression. We can see the forms evaluation order.

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

(with-result
  (guard (E ((begin
               (add-result 'guard-test)
               #t)
             (add-result 'guard-expr)
             E))
    (with-unwind-protection
        (lambda (why)
          (add-result 'cleanup))
      (lambda ()
        (add-result 'thunk-in)
        (raise 2)
        (add-result 'thunk-out)
        1))))
⇒ (2 (thunk-in guard-test cleanup guard-expr))

The following example with two nested guard uses and two nested dynamic-wind calls, makes it even more clear the sequence of forms evaluation. The clause of the inner guard has test expression returning #f, so the exception is re–raised with raise-continuable.

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

(with-result
  (guard (E ((begin
               (add-result 'outer-guard-test)
               #t)
             (add-result 'outer-guard-expr)
             E))
    (guard (E ((begin
                 (add-result 'inner-guard-test)
                 #f)
               (add-result 'inner-guard-expr)
               E))
      (dynamic-wind
          (lambda ()
            (add-result 'outer-before))
          (lambda ()
            (with-unwind-protection
                (lambda (why)
                  (add-result 'cleanup))
              (lambda ()
                (dynamic-wind
                    (lambda ()
                      (add-result 'inner-before))
                    (lambda ()
                      (add-result 'thunk-in)
                      (raise 2)
                      (add-result 'thunk-out)
                      1)
                    (lambda ()
                      (add-result 'inner-after))))))
          (lambda ()
            (add-result 'outer-after))))))
⇒ (2 (outer-before inner-before thunk-in inner-after outer-after
       inner-guard-test
       outer-before inner-before inner-after outer-after
       outer-guard-test
       outer-before inner-before inner-after cleanup outer-after
       outer-guard-expr))

Let’s describe the sequence of operations:

outer-before inner-before thunk-in

Everything goes fine until the ?thunk reaches thunk-in.

*

The ?thunk raises an exception. The exception handler of the inner guard is applied to the raised object.

inner-after outer-after

The continuation of the inner guard use is reinstated, the dynamic extent of the call to ?thunk is exited: the dynamic environment unwinds.

inner-guard-test

The clause test expression of the inner guard is evaluated in the dynamic environment of the guard use: it returns #f.

outer-before inner-before

The continuation of the inner guard exception handler is reinstated, the dynamic extent of the call to ?thunk is reentered: the dynamic environment is restored.

*

The exception is raised again by applying raise-continuable to the same raised object. The exception handler of the outer guard is applied to the raised object.

inner-after outer-after

The continuation of the outer guard use is reinstated, the dynamic extent of the call to ?thunk is exited: the dynamic environment unwinds.

outer-guard-test

The clause test expression of the outer guard is evaluated in the dynamic environment of the outer guard: it returns #t.

outer-before inner-before

The continuation of the of the outer guard exception handler is reinstated, the dynamic extent of the call to ?thunk is entered: the dynamic environment is restored.

inner-after cleanup outer-after

The continuation of the outer guard clause is reinstated, the the dynamic extent of the call to ?thunk is exited: the dynamic environment unwinds. While unwinding: the ?thunk is found terminated and the ?unwind-handler is called in the dynamic environment of the with-unwind-protection use.

outer-guard-expr

The clause expression of the outer guard is evaluated in the dynamic environment of the outer guard.

That’s Scheme!


Next: , Previous: , Up: iklib unwind-protect   [Index]