diff -r 87b735f86060 -r ddc05a611005 Tutorial/Tutorial2.thy --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Tutorial/Tutorial2.thy Fri Jan 21 21:58:51 2011 +0100 @@ -0,0 +1,277 @@ +theory Tutorial2 +imports Tutorial1 +begin + +(* fixme: put height example here *) + + +section {* Types *} + +nominal_datatype ty = + tVar "string" +| tArr "ty" "ty" ("_ \ _" [100, 100] 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 4 *} + +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 *} + + +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 a stronger induction principle that + has the usual "variable convention" already build in. The + following command derives this induction principle for us. + (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 -- {* has 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. +*} + + +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 *} + + +text {* unbind / inconsistency example *} + +inductive + unbind :: "lam \ lam \ bool" ("_ \ _" [60, 60] 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'" + +lemma unbind_simple: + shows "Lam [x].Var x \ Var x" + by auto + +equivariance unbind + +nominal_inductive unbind + avoids u_Lam: "x" + sorry + +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 + +lemma + shows "False" +proof - + have "atom x \ Lam [x]. Var x" by (simp add: lam.fresh) + then have "atom x \ Var x" using unbind_fake unbind_simple by auto + then show "False" by (simp add: lam.fresh fresh_at_base) +qed + +end