Exceptions
TAPL builds up an exception mechanism in three steps: raising exceptions, handling exceptions, and exceptions returning values. Below are the semantics of these three mechanisms using evaluation contexts.
Raising exceptions
Syntax:
t ::= ...
error
Evaluation:
E[error] ⟶ error (E-ConError)
Whereas the E-AppErr1 and E-AppErr2 rules
in TAPL “unwinds” the evaluation context one layer at a time, this rule
unwinds it all at once. (Actually, this rule is nondeterministic because
it can unwind any part of the evaluation context. How would you
modify the rules to make them deterministic?)
Handling exceptions
Syntax:
t ::= ...
error
try t with t
Now the evaluation rule for error should only unwind the
evaluation context up to the nearest try, not all the way
up to the top of the program as before. So we need two kinds of
evaluation contexts. The first has the same definition as if we hadn’t
added exceptions to the language:
F ::= [⋅]
F t
v F
The second can break through try:
E ::= F
F[E]
try E with t
Evaluation rules:
try v1 with t2 ⟶ v1 E-TryV
try F[error] with t2 ⟶ t2 E-TryConErr
Exceptions returning values
Syntax:
t ::= ...
raise t
try t with t
F ::= [·]
F t
v F
raise F
E ::= F
F[E]
try E with t
Evaluation:
try v1 with t2 ⟶ v1 E-TryV
try F[raise v11] with t2 ⟶ t2 v11 E-TryConRaise
In pure lambda calculus
In “exception-passing style,” every term t is replaced
with a sum. The inl values represent exceptions, and the
inr values represent ordinary values.
A term that doesn’t raise an exception becomes an
inr:
𝒯⟦x⟧ = inr x
𝒯⟦λx. t⟧ = inr λx. 𝒯⟦t⟧
But a raise expression becomes an inl. As
before, we define the translation just for variables:
𝒯⟦raise x⟧ = inl x
The translation of try is then:
𝒯⟦try t1 with t2⟧ = case 𝒯⟦t1⟧ of inl x ⇒ inr x,
inr x ⇒ 𝒯⟦t2 x⟧
Application of one variable to another is easy, as before.
𝒯⟦x1 x2⟧ = x1 x2
The translation of let captures the fact that if
t1 raises an exception, then t2 is
bypassed.
𝒯⟦let x = t1 in t2⟧ = case 𝒯⟦t1⟧ of inr x ⇒ 𝒯⟦t2⟧, inl x ⇒ inl x
Now we can derive the more general rules for raise and
applications.
𝒯⟦raise t⟧ = 𝒯⟦let x = t in raise x⟧
= case 𝒯⟦t⟧ of inl x ⇒ inl x,
inr x ⇒ inl x
𝒯⟦t1 t2⟧ = 𝒯⟦let x1 = t1 in let x2 = t2 in x1 x2⟧
= case 𝒯⟦t1⟧ of inl x ⇒ inl x,
inr x ⇒ (case 𝒯⟦t2⟧ of inl y ⇒ inl y, inr y ⇒ x y)