Next: unwind except 2, Previous: unwind dynamic, Up: unwind [Contents][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
, with
the exception that the “current exception handler” is the one that was previously installed
(see raise for a precise description of the operation).
The execution flow is not meant return to the caller of raise
, so the exception
handler has only two options:
(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.
(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 (only (mmck 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-handler (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 caught 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.
(import (only (mmck checks) with-result add-result)) (with-result (guard (E (else (add-result 'guard-else) E)) (with-unwind-handler (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 caught by
the clause of a guard
use; the clause has test and expression. We can see the forms
evaluation order.
(import (only (mmck checks) with-result add-result)) (with-result (guard (E ((begin (add-result 'guard-test) #t) (add-result 'guard-expr) E)) (with-unwind-handler (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
.
(import (only (mmck 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-handler (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-handler
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: unwind except 2, Previous: unwind dynamic, Up: unwind [Contents][Index]
This document describes version 0.1.0-devel.1 of MMCK Exceptional Conditions.