Next: iklib unwind-protect except 2, Previous: iklib unwind-protect dynamic, Up: iklib unwind-protect [Index]
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:
(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.
(import (vicare)) (call/cc (lambda (escape) (with-exception-handler escape (lambda () (with-exception-handler (lambda (E) (raise E)) (lambda () (raise 1))))))) ⇒ 1
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))
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: iklib unwind-protect except 2, Previous: iklib unwind-protect dynamic, Up: iklib unwind-protect [Index]