diff -r 3485111c7d62 -r e8736c1cdd7f Tutorial/Tutorial2s.thy --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Tutorial/Tutorial2s.thy Sat Jan 22 16:04:40 2011 -0600 @@ -0,0 +1,354 @@ + +theory Tutorial2s +imports Lambda +begin + +section {* Height of a Lambda-Term *} + +text {* + The theory Lambda defines the notions of capture-avoiding + substitution and the height of lambda terms. +*} + +thm height.simps +thm subst.simps + +subsection {* EXERCISE 7 *} + +text {* + Lets prove a property about the height of substitutions. + + Assume that the height of a lambda-term is always greater + or equal to 1. +*} + +lemma height_ge_one: + shows "1 \ height t" +by (induct t rule: lam.induct) (auto) + +text {* Remove the sorries *} + +theorem + shows "height (t[x ::= t']) \ height t - 1 + height t'" +proof (nominal_induct t avoiding: x t' rule: lam.strong_induct) + case (Var y) + have "1 \ height t'" using height_ge_one by simp + then show "height (Var y[x ::= t']) \ height (Var y) - 1 + height t'" by simp +next + case (App t1 t2) + have ih1: "height (t1[x::=t']) \ (height t1) - 1 + height t'" + and ih2: "height (t2[x::=t']) \ (height t2) - 1 + height t'" by fact+ + then show "height ((App t1 t2)[x ::= t']) \ height (App t1 t2) - 1 + height t'" by simp +next + case (Lam y t1) + have ih: "height (t1[x::=t']) \ height t1 - 1 + height t'" by fact + moreover + have vc: "atom y \ x" "atom y \ t'" by fact+ -- {* usual variable convention *} + ultimately + show "height ((Lam [y].t1)[x ::= t']) \ height (Lam [y].t1) - 1 + height t'" by simp +qed + + +section {* Types and the Weakening Lemma *} + +nominal_datatype ty = + tVar "string" +| tArr "ty" "ty" (infixr "\" 100) + + +text {* + Having defined them as nominal datatypes gives us additional + definitions and theorems that come with types (see below). + + We next define the type of typing contexts, a predicate that + defines valid contexts (i.e. lists that contain only unique + variables) and the typing judgement. +*} + +type_synonym ty_ctx = "(name \ ty) list" + +inductive + valid :: "ty_ctx \ bool" +where + v1[intro]: "valid []" +| v2[intro]: "\valid \; atom x \ \\\ valid ((x, T) # \)" + + +inductive + typing :: "ty_ctx \ lam \ ty \ bool" ("_ \ _ : _" [60, 60, 60] 60) +where + t_Var[intro]: "\valid \; (x, T) \ set \\ \ \ \ Var x : T" +| t_App[intro]: "\\ \ t1 : T1 \ T2; \ \ t2 : T1\ \ \ \ App t1 t2 : T2" +| t_Lam[intro]: "\atom x \ \; (x, T1) # \ \ t : T2\ \ \ \ Lam [x].t : T1 \ T2" + + +text {* + The predicate atom x \ \, read as x fresh for \, is defined by + Nominal Isabelle. It is defined for lambda-terms, products, lists etc. + For example we have: +*} + +lemma + fixes x::"name" + shows "atom x \ Lam [x].t" + and "atom x \ (t1, t2) \ atom x \ App t1 t2" + and "atom x \ Var y \ atom x \ y" + and "\atom x \ t1; atom x \ t2\ \ atom x \ (t1, t2)" + and "\atom x \ l1; atom x \ l2\ \ atom x \ (l1 @ l2)" + and "atom x \ y \ x \ y" + by (simp_all add: lam.fresh fresh_append fresh_at_base) + +text {* + We can also prove that every variable is fresh for a type. +*} + +lemma ty_fresh: + fixes x::"name" + and T::"ty" + shows "atom x \ T" +by (induct T rule: ty.induct) + (simp_all add: ty.fresh pure_fresh) + +text {* + In order to state the weakening lemma in a convenient form, we + using the following abbreviation. Abbreviations behave like + definitions, except that they are always automatically folded + and unfolded. +*} + +abbreviation + "sub_ty_ctx" :: "ty_ctx \ ty_ctx \ bool" ("_ \ _" [60, 60] 60) +where + "\1 \ \2 \ \x. x \ set \1 \ x \ set \2" + + +subsection {* EXERCISE 8 *} + +text {* + Fill in the details and give a proof of the weakening lemma. +*} + +lemma + assumes a: "\1 \ t : T" + and b: "valid \2" + and c: "\1 \ \2" + shows "\2 \ t : T" +using a b c +proof (induct arbitrary: \2) + case (t_Var \1 x T) + have a1: "valid \1" by fact + have a2: "(x, T) \ set \1" by fact + have a3: "valid \2" by fact + have a4: "\1 \ \2" by fact + + show "\2 \ Var x : T" sorry +next + case (t_Lam x \1 T1 t T2) + have ih: "\\3. \valid \3; (x, T1) # \1 \ \3\ \ \3 \ t : T2" by fact + have a0: "atom x \ \1" by fact + have a1: "valid \2" by fact + have a2: "\1 \ \2" by fact + + show "\2 \ Lam [x].t : T1 \ T2" sorry +qed (auto) -- {* the application case is automatic*} + + +text {* + Despite the frequent claim that the weakening lemma is trivial, + routine or obvious, the proof in the lambda-case does not go + through smoothly. Painful variable renamings seem to be necessary. + But the proof using renamings is horribly complicated (see below). +*} + +equivariance valid +equivariance typing + +lemma weakening_NOT_TO_BE_TRIED_AT_HOME: + assumes a: "\1 \ t : T" + and b: "valid \2" + and c: "\1 \ \2" + shows "\2 \ t : T" +using a b c +proof (induct arbitrary: \2) + -- {* lambda case *} + case (t_Lam x \1 T1 t T2) + have fc0: "atom x \ \1" by fact + have ih: "\\3. \valid \3; (x, T1) # \1 \ \3\ \ \3 \ t : T2" by fact + -- {* we choose a fresh variable *} + obtain c::"name" where fc1: "atom c \ (x, t, \1, \2)" by (rule obtain_fresh) + -- {* we alpha-rename the abstraction *} + have "Lam [c].((c \ x) \ t) = Lam [x].t" using fc1 + by (auto simp add: lam.eq_iff Abs1_eq_iff flip_def) + moreover + -- {* we can then alpha rename the goal *} + have "\2 \ Lam [c].((c \ x) \ t) : T1 \ T2" + proof - + -- {* we need to establish *} + -- {* (1) (x, T1) # \1 \ (x, T1) # ((c \ x) \ \2) *} + -- {* (2) valid ((x, T1) # ((c \ x) \ \2)) *} + have "(1)": "(x, T1) # \1 \ (x, T1) # ((c \ x) \ \2)" + proof - + have "\1 \ \2" by fact + then have "(c \ x) \ ((x, T1) # \1 \ (x, T1) # ((c \ x) \ \2))" using fc0 fc1 + by (perm_simp) (simp add: flip_fresh_fresh) + then show "(x, T1) # \1 \ (x, T1) # ((c \ x) \ \2)" by (rule permute_boolE) + qed + moreover + have "(2)": "valid ((x, T1) # ((c \ x) \ \2))" + proof - + have "valid \2" by fact + then show "valid ((x, T1) # ((c \ x) \ \2))" using fc1 + by (auto simp add: fresh_permute_left atom_eqvt valid.eqvt) + qed + -- {* these two facts give us by induction hypothesis the following *} + ultimately have "(x, T1) # ((c \ x) \ \2) \ t : T2" using ih by simp + -- {* we now apply renamings to get to our goal *} + then have "(c \ x) \ ((x, T1) # ((c \ x) \ \2) \ t : T2)" by (rule permute_boolI) + then have "(c, T1) # \2 \ ((c \ x) \ t) : T2" using fc1 + by (perm_simp) (simp add: flip_fresh_fresh ty_fresh) + then show "\2 \ Lam [c].((c \ x) \ t) : T1 \ T2" using fc1 by auto + qed + ultimately show "\2 \ Lam [x].t : T1 \ T2" by simp +qed (auto) -- {* var and app cases, luckily, are automatic *} + + +text {* + The remedy is to use again a stronger induction principle that + has the usual "variable convention" already build in. The + following command derives this induction principle for the typing + relation. (We shall explain what happens here later.) +*} + +nominal_inductive typing + avoids t_Lam: "x" + unfolding fresh_star_def + by (simp_all add: fresh_Pair lam.fresh ty_fresh) + +text {* Compare the two induction principles *} + +thm typing.induct +thm typing.strong_induct -- {* note the additional assumption {atom x} \ c *} + +text {* + We can use the stronger induction principle by replacing + the line + + proof (induct arbitrary: \2) + + with + + proof (nominal_induct avoiding: \2 rule: typing.strong_induct) + + Try now the proof. +*} + +subsection {* EXERCISE 9 *} + +lemma weakening: + assumes a: "\1 \ t : T" + and b: "valid \2" + and c: "\1 \ \2" + shows "\2 \ t : T" +using a b c +proof (nominal_induct avoiding: \2 rule: typing.strong_induct) + case (t_Var \1 x T) -- {* variable case is as before *} + have "\1 \ \2" by fact + moreover + have "valid \2" by fact + moreover + have "(x, T)\ set \1" by fact + ultimately show "\2 \ Var x : T" by auto +next + case (t_Lam x \1 T1 t T2) + have vc: "atom x \ \2" by fact -- {* additional fact afforded by the strong induction *} + have ih: "\\3. \valid \3; (x, T1) # \1 \ \3\ \ \3 \ t : T2" by fact + have a0: "atom x \ \1" by fact + have a1: "valid \2" by fact + have a2: "\1 \ \2" by fact + have "valid ((x, T1) # \2)" using a1 vc by auto + moreover + have "(x, T1) # \1 \ (x, T1) # \2" using a2 by auto + ultimately + have "(x, T1) # \2 \ t : T2" using ih by simp + then show "\2 \ Lam [x].t : T1 \ T2" using vc by auto +qed (auto) -- {* app case *} + + + +section {* Unbind and an Inconsistency Example *} + +text {* + You might wonder why we had to discharge some proof + obligations in order to obtain the stronger induction + principle for the typing relation. (Remember for + lambda terms this stronger induction principle is + derived automatically.) + + This proof obligation is really needed, because if we + assume universally a stronger induction principle, then + in some cases we can derive false. This is "shown" below. +*} + + +inductive + unbind :: "lam \ lam \ bool" (infixr "\" 60) +where + u_Var[intro]: "Var x \ Var x" +| u_App[intro]: "App t1 t2 \ App t1 t2" +| u_Lam[intro]: "t \ t' \ Lam [x].t \ t'" + +text {* It is clear that the following judgement holds. *} + +lemma unbind_simple: + shows "Lam [x].Var x \ Var x" + by auto + +text {* + Now lets derive the strong induction principle for unbind. + The point is that we cannot establish the proof obligations, + therefore we force Isabelle to accept them by using "sorry". +*} + +equivariance unbind +nominal_inductive unbind + avoids u_Lam: "x" + sorry + +text {* + Using the stronger induction principle, we can establish + th follwoing bogus property. +*} + +lemma unbind_fake: + fixes y::"name" + assumes a: "t \ t'" + and b: "atom y \ t" + shows "atom y \ t'" +using a b +proof (nominal_induct avoiding: y rule: unbind.strong_induct) + case (u_Lam t t' x y) + have ih: "atom y \ t \ atom y \ t'" by fact + have asm: "atom y \ Lam [x]. t" by fact + have fc: "atom x \ y" by fact + then have in_eq: "x \ y" by (simp add: fresh_at_base) + then have "atom y \ t" using asm by (simp add: lam.fresh) + then show "atom y \ t'" using ih by simp +qed + +text {* + And from this follows the inconsitency: +*} + +lemma + shows "False" +proof - + have "atom x \ Lam [x]. Var x" by (simp add: lam.fresh) + moreover + have "Lam [x]. Var x \ Var x" using unbind_simple by auto + ultimately + have "atom x \ Var x" using unbind_fake by auto + then have "x \ x" by (simp add: lam.fresh fresh_at_base) + then show "False" by simp +qed + +end