15-312 Recitation #4: Runtime Errors 2002-09-18 Joshua Dunfield (joshuad@cs) Carnegie Mellon University We'll take a closer look at runtime errors by adding an explicit "error" construct. Recitation outline: - Review of runtime errors (from lecture) - Explicit "error" - Preservation and progress - The empty type Review from lecture: Introduce a new judgment form: "e aborts" Rules deriving it: ----------------- div(k1, 0) aborts e1 aborts -------------------- apply(e1, e2) aborts e2 aborts -------------------- apply(v1, e2) aborts ?: e1 aborts -------------------- let(e1, x.e2) aborts e_i aborts --------------------------------- o(v1,...v_i-1,e_i,...,e_n) aborts e1 aborts ------------------- if(e1,e2,e3) aborts Modify the statement of the Progress theorem: If |- e : tau then (i) e value, or (ii) e |-> e' for some e', or [new] (iii) e aborts Introduce a new construct error Add a rule ------------ Abort error aborts Now for the typing -- the obvious thing is to say that `error' has _any_ type: ---------------- ErrorTyp G |- error : tau The problem is then to extend the proofs of preservation and progress. For preservation, there is nothing to be done, since `error' by itself doesn't step to anything. For progress, recall that the proof was by rule induction on the derivation of |- e : tau, with one case for each typing rule. Clearly we must add a case for ErrorTyp, but this is easy. We need to show error value, or error |-> e' for some e', or error aborts. error aborts By Abort Are we done? No. Why not? The theorem we're proving has changed (condition (iii)). Therefore, the IH has changed. We need to look at every application of the IH. Take the case for IfTyp: |- e1 : bool |- e2 : tau |- e3 : tau ------------------------------------------ IfTyp |- if(e1, e2, e3) : tau The first thing in the proof was to apply the IH: e1 |-> e1' or e1 value By IH but now this has changed: e1 |-> e1' or e1 value or e1 aborts By IH So we need to handle the subcase in which e1 aborts. Fortunately, we can apply a rule above to conclude if(e1, e2, e3) aborts If we had forgotten that rule, the process of checking the progress proof would tell us exactly what's needed: a way to get from e1 aborts to if(e1, e2, e3) aborts. Note that we no longer have uniqueness of typing, since `error', `let x = 0 in error end', etc. can be given any type. How could this be fixed? One way is to change the syntax so the programmer has to explicitly annotate `error' with the intended type. We've seen how to add pairs, sums, and a unit type (1) to MinML. Another (sometimes useful) type is the void type (0), which is empty -- that is, there is no value v such that |- v : 0 is derivable. (We can certainly show that `error' has type 0, but `error' is not a value!) However, there *are* values of type tau -> 0. |- fun f(x:int):0 is f x end : int -> 0 Thus, the 0 type gives us a way to characterize non-terminating expressions. Note that the "void" type in C and Java is something different, closest to a unit type 1 (not quite the same: you can't have a C function that takes two arguments, one of which is void, for example). Are there values of type int * 0? Intuitively, no. To prove it, observe that the rule v1 value v2 value ------------------ (v1, v2) value is the only rule that can conclude that a pair is a value. So to construct a pair (v1, v2) of type int * 0, we would have to construct a value of type 0, which can't be done.