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))
guardThis 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-inEverything 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-afterThe continuation of the inner guard use is reinstated, the dynamic extent of the call to
?thunk is exited: the dynamic environment unwinds.
inner-guard-testThe clause test expression of the inner guard is evaluated in the dynamic environment of
the guard use: it returns #f.
outer-before inner-beforeThe 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-afterThe continuation of the outer guard use is reinstated, the dynamic extent of the call to
?thunk is exited: the dynamic environment unwinds.
outer-guard-testThe clause test expression of the outer guard is evaluated in the dynamic environment of
the outer guard: it returns #t.
outer-before inner-beforeThe 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-afterThe 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-exprThe 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.