Next: dynamic environment except, Previous: dynamic environment extent, Up: dynamic environment [Index]
guard
Vicare’s implementation of the guard
syntax is really
sophisticated because it has to deal with both the dynamic environment
requirements of R6RS and the unwind protection mechanism defined by
Vicare itself.
In a syntax use like:
(guard (E (?test0 ?expr0) (?test1 ?expr1) (else ?expr2)) ?body0 ?body ...)
if the ?body raises an exception: one of the clauses will
certainly be executed because there is an else
clause. The
?body might mutate the dynamic environment; all the ?test
and ?expr expressions must be evaluated in the dynamic environment
of the use of guard
.
In a syntax use like:
(guard (E (?test0 ?expr0) (?test1 ?expr1)) ?body0 ?body ...)
if all the ?test expressions evaluate to false: we must re–raise
the exception using raise-continuable
; so the syntax is
“almost” equivalent to:
(guard (E (?test0 ?expr0) (?test1 ?expr1) (else (raise-continuable E))) ?body0 ?body ...)
but: ?body might mutate the dynamic environment; all the
?test and ?expr expressions must be evaluated in the dynamic
environment of the use of guard
; the raise-continuable
in the else
clause must be evaluated the dynamic environment of
the ?body.
We must remember that, when using:
(with-exception-handler ?handler ?thunk)
the ?handler procedure is evaluated in the dynamic environment of
the ?thunk, minus the exception handler itself. So, in
pseudo–code, a syntax use with else
clause must be expanded as
follows:
(guard (E (?test0 ?expr0) (?test1 ?expr1) (else ?expr2)) ?body0 ?body ...) → (save-guard-continuation (with-exception-handler (lambda (E) (reinstate-guard-continuation (cond (?test0 ?expr0) (?test1 ?expr1) (else ?expr2)))) (lambda () ?body0 ?body ...)))
and, also in pseudo–code, a syntax use without else
clause
must be expanded as follows:
(guard (E (?test0 ?expr0) (?test1 ?expr1)) ?body0 ?body ...) → (save-guard-continuation (with-exception-handler (lambda (E) (save-exception-handler-continuation (reinstate-guard-continuation (cond (?test0 ?expr0) (?test1 ?expr1) (else (reinstate-exception-handler-continuation (raise-continuable E))))))) (lambda () ?body0 ?body ...)))
notice how, in the exception handler, we have to jump out and in the dynamic environment of the exception handler itself.
Let’s focus on unwind proteciton in the case of raised exception. When using:
(with-unwind-protection ?cleanup ?thunk)
the ?cleanup is associated to the dynamic extent of the call to ?thunk: when the dynamic extent is terminated (as defined by Vicare) the ?cleanup is called.
Vicare defines as termination event of a guard
’s
?body the execution of a guard
’s clause that does not
re–raise the exception. For a guard
use like:
(guard (E (?test0 ?expr0) (?test1 ?expr1) (else ?expr2)) ?body0 ?body ...)
we can imagine the pseudo–code:
(guard (E (?test0 (run-unwind-protection-cleanups) ?expr0) (?test1 (run-unwind-protection-cleanups) ?expr1) (else (run-unwind-protection-cleanups) ?expr2)) ?body0 ?body ...)
and for a guard
use like:
(guard (E (?test0 ?expr0) (?test1 ?expr1)) ?body0 ?body ...)
we can imagine the pseudo–code:
(guard (E (?test0 (run-unwind-protection-cleanups) ?expr0) (?test1 (run-unwind-protection-cleanups) ?expr1) (else (raise-continuable E))) ?body0 ?body ...)
By doing things this way: an exception raised by an ?expr does not
impede the execution of the cleanups. If a ?test raises an
exception the cleanups will not be run, and there is nothing we can do
about it; ?test expressions are usually calls to predicates that
recognise the condition type of E
, so the risk of error is
reduced.
So, in pseudo–code, a syntax use with else
clause must be
expanded as follows:
(guard (E (?test0 ?expr0) (?test1 ?expr1) (else ?expr2)) ?body0 ?body ...) → (save-guard-continuation (with-exception-handler (lambda (E) (reinstate-guard-continuation (cond (?test0 (run-unwind-protection-cleanups) ?expr0) (?test1 (run-unwind-protection-cleanups) ?expr1) (else (run-unwind-protection-cleanups) ?expr2)))) (lambda () ?body0 ?body ...)))
and, also in pseudo–code, a syntax use without else
clause
must be expanded as follows:
(guard (E (?test0 ?expr0) (?test1 ?expr1)) ?body0 ?body ...) → (save-guard-continuation (with-exception-handler (lambda (E) (save-exception-handler-continuation (reinstate-guard-continuation (cond (?test0 (run-unwind-protection-cleanups) ?expr0) (?test1 (run-unwind-protection-cleanups) ?expr1) (else (reinstate-exception-handler-continuation (raise-continuable E))))))) (lambda () ?body0 ?body ...)))
But how is run-unwind-protection-cleanups
implemented? To cause
the cleanups to be called we must cause an exit from the dynamic extent
of the ?thunks. This is a sophisticated operation implemented as
follows:
(define (run-unwind-protection-cleanups) (run-unwind-protection-cleanup-upon-exit? #t) (save-clause-expression-continuation (reinstate-exception-handler-continuation (reinstate-clause-expression-continuation))))
we jump in guard
’s exception handler dynamic environment then
immediately jump out in the guard
’s clause expression dynamic
environment.
Next: dynamic environment except, Previous: dynamic environment extent, Up: dynamic environment [Index]