header {* Formalization *} (*<*) theory Lam_ml imports Nominal LaTeXsugar begin (*>*) text_raw {* \label{chap:formal} *} text {* This chapter contains the full formalization of the strong normalization theorem for the $\lambda_{ml}$-calculus. The first section deals with the formalization of syntax, typing, and substitution. Section~\ref{sec:reduction} contains the formalization of the reduction relation. For technical reasons, the reduction relation needs to be stated with additional freshness requirements on the variables occurring in binding position. So we show that these freshness conditions do not change the relation being defined. Section~\ref{sec:SN-formal} contains a formal treatment of the inductive characterization of strong normalization. The rest of the Chapter deals with stacks, reducibility, and the normalization theorem. *} section {* The Calculus *} text_raw {* \label{sec:calc} *} text {* As explained in Section \ref{sec:isabelle-atoms}, we begin the theory file by declaring \isa{name} to be our only atom sort. Furthermore, we use a nominal datatype to represent the terms of the $\lambda_{ml}$-calculus, using the syntax introduced in Chapter~\ref{chap:toptop} *} atom_decl name nominal_datatype trm = Var "name" | App "trm" "trm" | Lam "\name\trm" ("\ _ . _" [0,120] 120) | To "trm" "\name\trm" ("_ to _ in _" [141,0,140] 140) | Ret "trm" ("[_]") text {* The weak and strong induction principles provided by the \isacommand{nominal-datatype} declaration above are listed in Figure~\ref{fig:induct}. The first rule is weak, because one has to prove the binder cases (third and fourth premise) for \textit{all} names, whereas the second rule only requires one to prove the binder cases for names which are suitably fresh. *} text_raw {* \begin{figure}[t] \textit{\textbf{trm.induct}}:\\[1ex] {\mprset{flushleft} \inferrule{ @{thm_style prem1 "trm.induct"}\\\\ @{thm_style prem2 "trm.induct"}\\\\ @{thm_style prem3 "trm.induct"}\\\\ @{thm_style prem4 "trm.induct"}\\\\ @{thm_style prem5 "trm.induct"} }{ @{thm_style concl "trm.induct"} }}\\ \ \\ \textit{\textbf{trm.strong-induct}}:\\[1ex] {\mprset{flushleft} \inferrule{ \twpage{@{thm_style[display] prem1 "trm.strong_induct"}}\\\\ \twpage{@{thm_style[display] prem2 "trm.strong_induct"}} \\\\ \twpage{@{thm_style[display] prem3 "trm.strong_induct"}}\\\\ \twpage{@{thm_style[display] prem4 "trm.strong_induct"}}\\\\ \twpage{@{thm_style[display] prem5 "trm.strong_induct"}} }{ {@{thm_style concl "trm.strong_induct"} }} } \caption{Induction principles for the term datatype} \label{fig:induct} \end{figure} *} text {* Note that the premises of these rules neither refer to the context \isa{?z}, nor to the variable \isa{?trm}, but to universally quantified variables of the same type. This means that any current facts referring to \isa{?z} or \isa{?trm} need to be replaced by new facts as explained in Section~\ref{sec:cases}. We add the injectivity principle for @{typ "trm"} to the default simpset and instantiate some of the lemmas from the parent theory Nominal to the current setting. *} declare trm.inject[simp] lemmas name_swap_bij = pt_swap_bij''[OF pt_name_inst at_name_inst] lemmas ex_fresh = exists_fresh'[OF fin_supp] text {* The second \isacommand{lemmas} statement produces the lemma $$\text{\isa{ex-fresh}:@{term[show_sorts] " \ (c::name) . c \ (z::'a::fs_name)"}}$$ where @{term "z"} can be instantiated to any finitely supported term. Some other frequently used, automatically derived facts can be found in Figure \ref{fig:nominal-facts}. Furthermore, we establish a useful variant of the other alpha renaming lemmas. *} (* text_raw {* \pagebreak[4] *} *) lemma alpha'' : fixes x y :: name and t::trm assumes a: "x \ t" shows "[y].t = [x].([(y,x)] \ t)" proof - from a have aux: "y \ [(y, x)] \ t" by (subst fresh_bij[THEN sym, of _ _ "[(x,y)]"]) (auto simp add: perm_swap calc_atm) thus ?thesis by(auto simp add: alpha perm_swap name_swap_bij fresh_bij) qed text_raw {* \begin{figure} \begin{tabular}{r@ { }l} alpha: & \begin{minipage}[t]{.8\textwidth} (@{thm_style lhs alpha[no_vars]}) = \\ (@{thm_style rhs alpha[no_vars]}) \end{minipage} \\ alpha': & \begin{minipage}[t]{.8\textwidth} (@{thm_style lhs "alpha'"[no_vars]}) = \\ (@{thm_style rhs "alpha'"[no_vars]}) \end{minipage} \\ abs-fresh: & \begin{minipage}{\textwidth}@{thm[display] "abs_fresh"(1)[no_vars]}\end{minipage} \\ fresh-atm: & \begin{minipage}{\textwidth}@{thm[display] "fresh_atm"[no_vars]}\end{minipage} \\ fresh-prod: & \begin{minipage}{\textwidth}@{thm[display] "fresh_prod"[no_vars]}\end{minipage} \\ exists-fresh': & \begin{minipage}{\textwidth}@{thm[display] "exists_fresh'"[no_vars]}\end{minipage} \end{tabular} \caption{Some automatically derived facts} \label{fig:nominal-facts} \end{figure} *} subsection {* Typing *} text {* Even though our types do not involve binders, we still need to formalize them as nominal datatypes to obtain a permutation action. This is required to establish equivariance of the typing relation. *} nominal_datatype ty = TBase | TFun "ty" "ty" (infix "\" 200) | T "ty" text{* Since, as explained in Section~\ref{sec:isabelle-atoms}, we cannot use typed variables; we have to formalize typing contexts. Isabelle does not provide a type for finite functions, hence typing contexts are formalized as lists. A context is \textit{valid} if no name occurs twice. *} inductive valid :: "(name\ty) list \ bool" where v1[intro]: "valid []" | v2[intro]: "\valid \;x\\\\ valid ((x,\)#\)" equivariance valid lemma fresh_ty: fixes x :: name and \::ty shows "x \ \" by (induct \ rule: ty.induct) (auto) lemma fresh_context: fixes \ :: "(name\ty)list" assumes a: "x \ \" shows "\(\ \ . (x,\)\set \)" using a by (induct \) (auto simp add: fresh_prod fresh_list_cons fresh_atm) inductive typing :: "(name\ty) list\trm\ty\bool" ("_ \ _ : _" [60,60,60] 60) where t1[intro]: "\valid \; (x,\)\set \\ \ \ \ Var x : \" | t2[intro]: "\\ \ s : \\\; \ \ t : \\ \ \ \ App s t : \" | t3[intro]: "\x \ \; ((x,\)#\) \ t : \\ \ \ \ \ x . t : \\\" | t4[intro]: "\ \ \ s : \ \ \ \ \ [s] : T \" | t5[intro]: "\x \ (\,s); \ \ s : T \ ; ((x,\)#\) \ t : T \ \ \ \ \ s to x in t : T \" equivariance typing nominal_inductive typing by(simp_all add: abs_fresh fresh_ty) text {* Except for the explicit requirement that contexts be valid in the variable case and the freshness requirement on $s$ in \isa{t5}, this typing relation is a direct translation of the original typing relation in \cite{TT-lifting} to the setting using contexts. The \isacommand{nominal-inductive} command derives the strong induction and case rules described in Section~\ref{sec:rule-induction}. The strong induction principle will be used in the proof of the fundamental theorem of logical relations.*} subsection {* Substitution *} text {* Here we introduce substitution on the terms defined above. Since we need parallel substitution for the fundamental theorem, we define it first and introduce ordinary substitution as an abbreviation. Unfortunately, the function type @{typ "name => trm"} is not finitely supported. Thus, as was the case with contexts, the easiest approach is to formalize substitutions as lists. *} fun lookup :: "(name\trm) list \ name \ trm" where "lookup [] x = Var x" | "lookup ((y,e)#\) x = (if x=y then e else lookup \ x)" lemma lookup_eqvt[eqvt]: fixes pi::"name prm" and \::"(name\trm) list" and x::"name" shows "pi \ (lookup \ x) = lookup (pi \ \) (pi \ x)" by (induct \) (auto simp add: eqvts) nominal_primrec psubst :: "(name\trm) list \ trm \ trm" ("_<_>" [95,95] 205) where "\ = lookup \ x" | "\ = App (\) (\)" | "x \ \ \ \<\ x .s> = \ x . (\)" | "\<[t]> = [\]" | "\ x \ \ ; x \ t \ \ \ = (\) to x in (\)" by(finite_guess+ , (simp add: abs_fresh)+ , fresh_guess+) lemma psubst_eqvt[eqvt]: fixes pi::"name prm" shows "pi \ (\) = (pi \ \)<(pi \ t)>" by(nominal_induct t avoiding: \ rule:trm.strong_induct) (auto simp add: eqvts fresh_bij) text {* The lemma \isa{psubst-eqvt} states that substitution, like all our constructions, is equivariant. Having defined parallel substitution, we define substitution for a single variable as an abbreviation of the parallel case. Furthermore, we show the usual defining equations as a lemma and add it to the default simpset. The effect of this is that single variable substitution behaves just as if defined directly, and also interacts smoothly with the parallel case. *} abbreviation subst :: "trm \ name \ trm \ trm" ("_[_::=_]" [200,100,100] 200) where "t[x::=t'] \ ([(x,t')])" lemma subst[simp]: shows "(Var x)[y::=v] = (if x = y then v else Var x)" and "(App s t)[y::=v] = App (s[y::=v]) (t[y::=v])" and "x \ (y,v) \ (\ x . t)[y::=v] = \ x .t[y::=v]" and "x \ (s,y,v) \ (s to x in t)[y::=v] = s[y::=v] to x in t[y::=v]" and "([s])[y::=v] = [s[y::=v]]" by(simp_all add: fresh_list_cons fresh_list_nil) subsection {* Facts about substitution *} text {* To be able to work comfortably with substitution, we need a couple of lemmas about substitution that concern the interaction of substitution and freshness. *} lemma subst_rename: assumes a: "y \ t" shows "([(y,x)]\t)[y::=v] = t[x::=v]" using a by(nominal_induct t avoiding: x y v rule: trm.strong_induct) (auto simp add: calc_atm fresh_atm abs_fresh fresh_prod fresh_aux) lemmas subst_rename' = subst_rename[THEN sym] lemma forget: "x \ t \ t[x::=v] = t" by(nominal_induct t avoiding: x v rule: trm.strong_induct) (auto simp add: abs_fresh fresh_atm) lemma fresh_fact: fixes x::"name" assumes x: "x \ v" "x \ t" shows "x \ t[y::=v]" using x by(nominal_induct t avoiding: x y v rule: trm.strong_induct) (auto simp add: abs_fresh fresh_atm) lemma fresh_fact': fixes x::"name" assumes x: "x \ v" shows "x \ t[x::=v]" using x by(nominal_induct t avoiding: x v rule: trm.strong_induct) (auto simp add: abs_fresh fresh_atm) lemma subst_lemma: assumes a: "x\y" and b: "x \ u" shows "s[x::=v][y::=u] = s[y::=u][x::=v[y::=u]]" using a b by(nominal_induct s avoiding: x y u v rule: trm.strong_induct) (auto simp add: fresh_fact forget) lemma id_subs: shows "t[x::=Var x] = t" by(nominal_induct t avoiding: x rule:trm.strong_induct) auto text {* In addition to the facts on simple substitution we also need some facts on parallel substitution. In particular we want to be able to extend a parallel substitution with an ordinary one. *} lemma lookup_fresh: fixes z::"name" assumes "z\\" "z\x" shows "z\ lookup \ x" using assms by(induct rule: lookup.induct) (auto simp add: fresh_list_cons) lemma lookup_fresh': assumes a: "z\\" shows "lookup \ z = Var z" using a by (induct rule: lookup.induct) (auto simp add: fresh_list_cons fresh_prod fresh_atm) lemma psubst_fresh_fact: fixes x :: name assumes a: "x \ t" and b: "x \ \" shows "x \ \" using a b by(nominal_induct t avoiding: \ x rule:trm.strong_induct) (auto simp add: lookup_fresh abs_fresh) lemma psubst_subst: assumes a: "x \ \" shows "\[x::=s] = ((x,s)#\)" using a by(nominal_induct t avoiding: \ x s rule: trm.strong_induct) (auto simp add: fresh_list_cons fresh_atm forget lookup_fresh lookup_fresh' fresh_prod psubst_fresh_fact) section {* The Reduction Relation *} text_raw {* \label{sec:reduction} *} text {* With substitution in place, we can now define the reduction relation on $\lambda_{ml}$-terms. To derive strong induction and case rules, all the rules must be vc-compatible. In the case of the reduction relation stated in Chapter~\ref{chap:toptop}, this requires some additional freshness conditions. Note that in this particular case the additional freshness conditions only serve the technical purpose of automatically deriving strong reasoning principles. To show that the version with freshness conditions defines the same relation as the one in Figure~\ref{fig:lam_ml}, we also state this version and prove equality of the two relations. *} inductive std_reduction :: "trm \ trm \ bool" ("_ \ _" [80,80] 80) where std_r1[intro!]:"s \ s' \ App s t \ App s' t" | std_r2[intro!]:"t \ t' \ App s t \ App s t'" | std_r3[intro!]:"App (\ x . t) s \ t[x::=s]" | std_r4[intro!]:"t \ t' \ \ x . t \ \ x . t'" | std_r5[intro!]:"x \ t \ \ x . App t (Var x) \ t" | std_r6[intro!]:"\ s \ s' \ \ s to x in t \ s' to x in t" | std_r7[intro!]:"\ t \ t' \ \ s to x in t \ s to x in t'" | std_r8[intro!]:"[s] to x in t \ t[x::=s]" | std_r9[intro!]:"x \ s \ s to x in [Var x] \ s" | std_r10[intro!]: "\ x \ y; x \ u\ \ (s to x in t) to y in u \ s to x in (t to y in u)" | std_r11[intro!]: "s \ s' \ [s] \ [s']" inductive reduction :: "trm \ trm \ bool" ("_ \ _" [80,80] 80) where r1[intro!]:"s \ s' \ App s t \ App s' t" | r2[intro!]:"t \ t' \ App s t \ App s t'" | r3[intro!]:"x \ s \ App (\ x . t) s \ t[x::=s]" | r4[intro!]:"t \ t' \ \ x . t \ \ x . t'" | r5[intro!]:"x \ t \ \ x . App t (Var x) \ t" | r6[intro!]:"\ x \ (s,s') ; s \ s' \ \ s to x in t \ s' to x in t" | r7[intro!]:"\ x \ s ; t \ t' \ \ s to x in t \ s to x in t'" | r8[intro!]:"x \ s \ [s] to x in t \ t[x::=s]" | r9[intro!]:"x \ s \ s to x in [Var x] \ s" | r10[intro!]: "\ x \ (y,s,u) ; y \ (s,t) \ \ (s to x in t) to y in u \ s to x in (t to y in u)" | r11[intro!]: "s \ s' \ [s] \ [s']" equivariance reduction nominal_inductive reduction by(auto simp add: abs_fresh fresh_fact' fresh_prod fresh_atm) text {* In order to show adequacy, the extra freshness conditions in the rules \isa{r3}, \isa{r6}, \isa{r7}, \isa{r8}, \isa{r9}, and \isa{r10} need to be discharged. Furthermore, we make the classical reasoners, used in \isa{auto} and \isa{blast}, aware of the new rules by adding them as introduction rules to the classical rule set. To avoid conflicts, the old rules are then deleted. *} text_raw {* \label{pg:alpha-begin} *} lemma r3'[intro!]: "App (\ x . t) s \ t[x::=s]" proof - obtain x'::name where s: "x' \ s" and t: "x' \ t" using ex_fresh[of "(s,t)"] by (auto simp add: fresh_prod) from t have "App (\ x . t) s = App (\ x' . ([(x,x')] \ t)) s" by (simp add: alpha'') also from s have "\ \ ([(x, x')] \ t)[x'::=s]" .. also have "\ = t[x::=s]" using t by (auto simp add: subst_rename') (metis perm_swap) finally show ?thesis . qed declare r3[rule del] lemma r6'[intro]: fixes s :: trm assumes r: "s \ s'" shows "s to x in t \ s' to x in t" using assms proof - obtain x'::name where s: "x' \ (s, s')" and t: "x' \ t" using ex_fresh[of "(s,s',t)"] by (auto simp add: fresh_prod) from t have "s to x in t = s to x' in ([(x,x')] \ t)" by (simp add: alpha'') also from s r have "\ \ s' to x' in ([(x, x')] \ t)" .. also from t have "\ = s' to x in t" by (simp add: alpha'') finally show ?thesis . qed declare r6[rule del] lemma r7'[intro]: fixes t :: trm assumes "t \ t'" shows "s to x in t \ s to x in t'" using assms proof - obtain x'::name where f: "x' \ t" "x' \ t'" "x' \ s" "x' \ x" using ex_fresh[of "(t,t',s,x)"] by(auto simp add:fresh_prod) hence a: "s to x in t = s to x' in ([(x,x')] \ t)" by (auto simp add: alpha'') from assms have "([(x,x')] \ t) \ [(x,x')] \ t'" by (simp add: eqvts) hence r: "s to x' in ([(x,x')] \ t) \ s to x' in ([(x,x')] \ t')" using f by auto from f have "s to x in t' = s to x' in ([(x,x')] \ t')" by (auto simp add: alpha'') with a r show ?thesis by (simp del: trm.inject) qed declare r7[rule del] lemma r8'[intro!]: "[s] to x in t \ t[x::=s]" proof - obtain x'::name where s: "x' \ s" and t: "x' \ t" using ex_fresh[of "(s,t)"] by (auto simp add: fresh_prod) from t have "[s] to x in t = [s] to x' in ([(x,x')] \ t)" by (simp add: alpha'') also from s have "\ \ ([(x, x')] \ t)[x'::=s]" .. also have "\ = t[x::=s]" using t by (auto simp add: subst_rename') (metis perm_swap) finally show ?thesis . qed declare r8[rule del] lemma r9'[intro!]: "s to x in [Var x] \ s" proof - obtain x'::name where f: "x' \ s" "x' \ x" using ex_fresh[of "(s,x)"] by(auto simp add:fresh_prod) hence "s to x' in [Var x'] \ s" by auto moreover have "s to x' in ([Var x']) = s to x in ([Var x])" by (auto simp add: alpha fresh_atm swap_simps) ultimately show ?thesis by simp qed declare r9[rule del] text {* While discharging these freshness conditions is easy for rules involving only one binder it unfortunately becomes quite tedious for the assoc rule \isa{r10}. This is due to the complex binding structure of this rule which includes \textit{four} binding occurrences of two different names. Furthermore, the binding structure changes from the left to the right: On the left hand side, $x$ is only bound in $t$, whereas on the right hand side the scope of $x$ extends over the whole term @{term "(t to y in u)"}. *} lemma r10'[intro!]: assumes xf: "x \ y" "x \ u" shows "(s to x in t) to y in u \ s to x in (t to y in u)" proof - obtain y'::name -- "suitably fresh" where y: "y' \ s" "y' \ x" "y' \ t" "y' \ u" using ex_fresh[of "(s,x,t,u,[(x, x')] \ t)"] by (auto simp add: fresh_prod) obtain x'::name where x: "x' \ s" "x' \ y'" "x' \ y" "x' \ t" "x' \ u" "x' \ ([(y,y')] \ u)" using ex_fresh[of "(s,y',y,t,u,([(y,y')] \ u))"] by (auto simp add: fresh_prod) from x y have yaux: "y' \ [(x, x')] \ t" by(simp add: fresh_left perm_fresh_fresh fresh_atm) have "(s to x in t) to y in u = (s to x in t) to y' in ([(y,y')] \ u)" using `y' \ u` by (simp add: alpha'') also have "\ = (s to x' in ([(x,x')] \ t)) to y' in ([(y,y')] \ u)" using `x' \ t` by (simp add: alpha'') also have "\ \ s to x' in (([(x,x')] \ t) to y' in ([(y,y')] \ u))" using x y yaux by (auto simp add: fresh_prod) also have " \ = s to x' in (([(x,x')] \ t) to y in u)" using `y' \ u` by (simp add: abs_fun_eq1 alpha'') also have "\ = s to x in (t to y in u)" proof (subst trm.inject) from xf x have swap: "[(x,x')] \ y = y" "[(x,x')] \ u = u " by(auto simp add: fresh_atm perm_fresh_fresh ) with x show "s = s \ [x'].([(x, x')] \ t) to y in u = [x].t to y in u" by (auto simp add: alpha''[of x' _ x] abs_fresh abs_fun_eq1 swap) qed finally show ?thesis . qed declare r10[rule del] text_raw {* \label{pg:alpha-end} *} text {* Since now all the introduction rules of the vc-compatible reduction relation exactly match their standard counterparts, both directions of the adequacy proof are trivial inductions. *} theorem adequacy: "s \ t = s \ t" by (auto elim:reduction.induct std_reduction.induct) text {* Next we show that the reduction relation preserves freshness and is in turn preserved under substitution. *} lemma reduction_fresh: fixes x::"name" assumes r: "t \ t'" shows "x \ t \ x \ t'" using r by(nominal_induct t t' avoiding: x rule: reduction.strong_induct) (auto simp add: abs_fresh fresh_fact fresh_atm) lemma reduction_subst: assumes a: " t \ t' " shows "t[x::=v] \ t'[x::=v]" using a by(nominal_induct t t' avoiding: x v rule: reduction.strong_induct) (auto simp add: fresh_atm fresh_fact subst_lemma fresh_prod abs_fresh) section {* Strong Normalization *} text_raw {* \label{sec:SN-formal} *} text {* Next we need to formalize what it means for a term to be strongly normalizing. As already motivated in Section \ref{sec:SN}, we use an inductive variant of strong normalization, It allows for inductive proofs on terms being strongly normalizing, without establishing that the reduction relation is finitely branching. *} inductive SN :: "trm \ bool" where SN_intro: "(\ t' . t \ t' \ SN t') \ SN t" text {* It remains to be shown that this definition actually excludes infinite sequences of reductions. We define a term $t$ to be diverging, written @{term "DIV t"}, if there is some infinite sequence $S$ of reductions beginning at $t$. *} constdefs DIV :: "trm \ bool" "DIV t \ \ (S::nat \ trm) . t = S 0 \ (\ n . S n \ S (n + 1))" theorem "SN t \ \ DIV t" proof (induct rule:SN.induct) case (SN_intro t) have ih: "\t'. t \ t' \ \ DIV t'" by fact moreover have "DIV t \ \ t' . t \ t' \ DIV t'" proof - assume "DIV t" from this obtain S::"nat\trm" where S: "t = S 0 \ (\ n . S n \ S (n + 1))" unfolding DIV_def .. let ?t = "S 1" let ?S = "\ n . S (n + 1)" from S have " t \ ?t" by auto moreover { from S have "?t = ?S 0 \ (\ n . ?S n \ ?S (n + 1))" by auto hence "DIV ?t" unfolding DIV_def by auto} ultimately show ?thesis by blast qed ultimately show "\ DIV t" using ih by blast qed text {* Incidentally, the converse direction holds as well. Our proof requires the axiom of choice and hence would not have been possible in a logic where everything has finite support. The claim could probably also be proven without the use of choice, but this would, for example, require the construction of an order on the set of terms to be able to select the least successor that is not \isa{SN}. *} theorem "\ SN t \ DIV t" proof - fix t assume t: "\ SN t" let ?NSN = "{ t . \ SN t }" have "\ t \ ?NSN . \ t' . t \ t' \ \ SN t'" by (auto intro: SN_intro) hence " \ f . \ t \ ?NSN . t \ f t \ \ SN (f t)" by (rule bchoice) from this obtain f where f: "\ t \ ?NSN . t \ f t \ \ SN (f t)" .. let ?S = "\ n . (f^n) t" { fix n from t f have "?S n \ ?S (n + 1) \ \ SN (?S (n + 1))" by (induct n) auto } hence "t = ?S 0 \ (\ n . ?S n \ ?S (n + 1))" by auto thus "DIV t" unfolding DIV_def by(rule exI[where x="?S"]) qed text{* For the formalization, we merely need that strong normalization is preserved under reduction and some lemmas on normal terms. *} lemma SN_preserved[intro]: assumes a: "SN t" "t \ t'" shows "SN t'" using a by (cases) (auto) constdefs "NORMAL" :: "trm \ bool" "NORMAL t \ \(\t'. t \ t')" lemma normal_var: "NORMAL (Var x)" unfolding NORMAL_def by (auto elim: reduction.cases) lemma normal_implies_sn : "NORMAL s \ SN s" unfolding NORMAL_def by(auto intro: SN_intro) section {* Stacks *} text{* As explained in Chapter~\ref{sec:toptop}, the monadic type structure of the $\lambda_{ml}$-calculus does not lend itself to an easy definition of a logical relation along the type structure of the calculus. Therefore, we need to introduce stacks as an auxiliary notion to handle the monadic type constructor $T$. Stacks can be thought of as lists of term abstractions @{term "[x].t"}. Our notation for stacks is chosen with this resemblance in mind. *} nominal_datatype stack = Id | St "\name\trm" "stack" ("[_]_\_") lemma stack_exhaust : fixes c :: "'a::fs_name" shows "k = Id \ (\ y n l . y \ l \ y \ c \ k = [y]n\l)" by(nominal_induct k avoiding: c rule: stack.strong_induct) (auto) nominal_primrec length :: "stack \ nat" ( "|_|") where "|Id| = 0" | "y \ L \ length ([y]n\L) = 1 + |L|" by(finite_guess+,auto simp add: fresh_nat,fresh_guess) subsection {* Stack dismantling *} text{* Together with the stack datatype, we introduce the notion of dismantling a term onto a stack. Unfortunately, the dismantling operation has no easy primitive recursive formulation. The Nominal package, however, only provides a recursion combinator for primitive recursion. This means that for dismantling one has to prove pattern completeness, right uniqueness, and termination explicitly. This takes a little more effort than using \isacommand{nominal{\isacharunderscore}primrec} and the mostly canonical proof to discharge the finite support and freshness requirements. However, once this has been done, the defining equations can be used as simplification rules just as if defined with \isacommand{nominal{\isacharunderscore}primrec}. *} function dismantle :: "trm \ stack \ trm" ("_ \ _" [160,160] 160) where "t \ Id = t" | "x \ (K,t) \ t \ ([x]s\K) = (t to x in s) \ K" proof - -- "pattern completeness" fix P :: bool and arg::"trm \ stack" assume id: "\t. arg = (t, stack.Id) \ P" and st: "\x K t s. \x \ (K, t); arg = (t, [x]s\K)\ \ P" { assume "snd arg = Id" hence P by (metis id[where t="fst arg"] surjective_pairing) } moreover { fix y n L assume "snd arg = [y]n\L" "y \ (L, fst arg)" hence P by (metis st[where t="fst arg"] surjective_pairing) } ultimately show P using stack_exhaust[of "snd arg" "fst arg"] by auto next -- "right uniqueness " -- "only the case of the second equation matching both args needs to be shown." fix t t' :: trm and x x' :: name and s s' :: trm and K K' :: stack let ?g = dismantle_sumC -- "graph of dismantle" assume "x \ (K, t)" "x' \ (K', t')" and "(t, [x]s\K) = (t', [x']s'\K')" thus "?g (t to x in s, K) = ?g (t' to x' in s', K')" by (auto intro!: arg_cong[where f="?g"] simp add: stack.inject) qed (simp_all add: stack.inject) -- "all other cases are trivial" text {* Note the use of \isa{metis} for the relatively simple goals above. The reason for this is that \isa{simp} and \isa{auto} diverge if one adds surjective pairing (@{thm surjective_pairing[no_vars]} to the simpset whereas \isa{metis} finds a proof within a fraction of a second. Afterwards, we just have to establish termination which is simple as the length of $K$ decreases with every recursive call. *} termination dismantle by(relation "measure (\(t,K). |K| )")(auto) text{* Like all our constructions, dismantling is equivariant. Also, freshness can be pushed over dismantling, and the freshness requirement in the second defining equation is not needed *} lemma dismantle_eqvt[eqvt]: fixes pi :: "(name \ name) list" shows "pi \ (t \ K) = (pi \ t) \ (pi \ K)" by(nominal_induct K avoiding: pi t rule:stack.strong_induct) (auto simp add: eqvts fresh_bij) lemma dismantle_fresh[iff]: fixes x :: name shows "(x \ (t \ k)) = (x \ t \ x \ k)" by(nominal_induct k avoiding: t x rule: stack.strong_induct) (simp_all) lemma dismantle_simp[simp]: "s \ [y]n\L = (s to y in n) \ L" proof - obtain x::name where f: "x \ s" "x \ L" "x \ n" using ex_fresh[of "(s,L,n)"] by(auto simp add:fresh_prod) hence t: "s to y in n = s to x in ([(y,x)] \ n)" by(auto simp add: alpha'') from f have "[y]n\L = [x]([(y,x)] \ n)\L" by (auto simp add: stack.inject alpha'') hence "s \ [y]n\L = s \ [x]([(y,x)] \ n)\L" by simp also have " \ = (s to y in n) \ L" using f t by(simp del:trm.inject) finally show ?thesis . qed subsection {* Reduction and substitution for stacks *} text {* We also need a notion of reduction on stacks. This reduction relation allows us to define strong normalization not only for terms but also for stacks and is needed to prove the properties of the logical relation later on. *} constdefs stack_reduction :: "stack \ stack \ bool" (" _ \ _ ") "k \ k' \ \ (t::trm) . (t \ k) \ (t \ k')" (* The intuition behind this construction is that if a term @{term "t \ k"} can make a step to @{term "t \ k'"} regardless of the term $t$, then this reduction depends only on $k$. Hence, it is natural to consider this as a reduction of the stack itself. *) text {* While one could certainly obtain the same reduction relation by explicitly stating reduction rules, the given definition provides a rather canonical way for lifting properties of the term reduction relation to the reduction relation on stacks. One example, shown below, is that freshness is preserved under stack reduction. *} (* lemma stack_reduction_eqvt[eqvt]: fixes pi :: "(name \ name) list" and k :: stack assumes r: "k \ k'" shows "(pi \ k) \ (pi \ k')" unfolding stack_reduction_def proof fix t :: trm have "(rev pi \ t) \ k \ (rev pi \ t) \ k' " using r unfolding stack_reduction_def by auto hence "(pi \ ((rev pi \ t) \ k)) \ (pi \ ((rev pi \ t) \ k')) " by (rule reduction.eqvt) thus "(t \ (pi \ k)) \ (t \ (pi \ k'))" by(auto simp only: eqvts perm_pi_simp) qed *) lemma stack_reduction_fresh: fixes k :: stack and x :: name assumes r : "k \ k'" and f :"x \ k" shows "x \ k'" proof - from ex_fresh[of x] obtain z::name where f': "z \ x" .. from r have "Var z \ k \ Var z \ k'" unfolding stack_reduction_def .. moreover from f f' have "x \ Var z \ k" by(auto simp add: fresh_atm) ultimately have "x \ Var z \ k'" by(rule reduction_fresh) thus "x \ k'" by simp qed lemma dismantle_red[intro]: fixes m :: trm assumes r: " m \ m'" shows "m \ k \ m' \ k" using r by (nominal_induct k avoiding: m m' rule:stack.strong_induct) auto (* subsection {* Substitution for stacks *} *) text {* Next we define a substitution operation for stacks. The main purpose of this is to distribute substitution over dismantling. *} nominal_primrec ssubst :: "name \ trm \ stack \ stack" where "ssubst x v Id = Id" | " y \ (k,x,v) \ ssubst x v ([y]n\k) = [y](n[x::=v])\(ssubst x v k)" by(finite_guess+ , (simp add: abs_fresh)+ , fresh_guess+) lemma ssubst_fresh: fixes y :: name assumes " y \ (x,v,k) " shows "y \ ssubst x v k" using assms by(nominal_induct k avoiding: y x v rule: stack.strong_induct) (auto simp add: fresh_prod fresh_atm abs_fresh fresh_fact) lemma ssubst_forget: fixes x :: name assumes "x \ k" shows "ssubst x v k = k" using assms by(nominal_induct k avoiding: x v rule: stack.strong_induct) (auto simp add: abs_fresh fresh_atm forget) lemma subst_dismantle[simp]: "(t \ k)[x ::= v] = (t[x::=v]) \ ssubst x v k" by(nominal_induct k avoiding: t x v rule: stack.strong_induct) (auto simp add: ssubst_fresh fresh_prod fresh_fact) section {* Reducibility for Terms and Stacks *} text_raw {* \label{sec:reducibility-formal} *} text {* Following \cite{SN.thy}, we formalize the logical relation as a function @{term "RED"} of type @{typ "ty \ trm set"} for the term part and accordingly @{term SRED} of type @{typ "ty \ stack set"} for the stack part of the logical relation. Showing that these mutually recursive functions terminate is therefore equivalent to showing that the logical relation is correctly defined on the type structure. *} lemma ty_exhaust: "ty = TBase \ (\ \ \ . ty = \ \ \) \ (\ \ . ty = T \)" by(induct ty rule:ty.induct) (auto) function RED :: "ty \ trm set" and SRED :: "ty \ stack set" where "RED (TBase) = {t. SN(t)}" | "RED (\\\) = {t. \ u \ RED \ . (App t u) \ RED \ }" | "RED (T \) = {t. \ k \ SRED \ . SN(t \ k) }" | "SRED \ = {k. \ t \ RED \ . SN ([t] \ k) }" by(auto simp add: ty.inject, case_tac x rule: sum.exhaust,insert ty_exhaust) (blast)+ text {* This is the second non-primitive function in the formalization. Since types do not involve binders, pattern completeness and right uniqueness are mostly trivial. The termination argument is not as simple as for the dismantling function, because the definiton of @{term "SRED \"} involves a recursive call to @{term "RED \"} of the same size. *} nominal_primrec tsize :: "ty \ nat" where "tsize TBase = 1" | "tsize (\\\) = 1 + tsize \ + tsize \" | "tsize (T \) = 1 + tsize \" by (rule TrueI)+ text {* In the termination argument below, @{term "Inl \"} corresponds to the call @{term "RED \"}, whereas @{term "Inr \"} corresponds to @{term "SRED \"} *} termination RED by(relation "measure (\ x . case x of Inl \ \ 2 * tsize \ | Inr \ \ 2 * tsize \ + 1)") (auto) section {* Properties of the Reducibility Relation *} text {* After defining the logical relations we need to prove that the relation implies strong normalization, is preserved under reduction, and satisfies the head expansion property. *} constdefs NEUT :: "trm \ bool" "NEUT t \ (\a. t = Var a) \ (\t1 t2. t = App t1 t2)" constdefs "CR1" :: "ty \ bool" "CR1 \ \ \t. (t\RED \ \ SN t)" "CR2" :: "ty \ bool" "CR2 \ \ \t t'. (t\RED \ \ t \ t') \ t'\RED \" "CR3_RED" :: "trm \ ty \ bool" "CR3_RED t \ \ \t'. t \ t' \ t'\RED \" "CR3" :: "ty \ bool" "CR3 \ \ \t. (NEUT t \ CR3_RED t \) \ t\RED \" "CR4" :: "ty \ bool" "CR4 \ \ \t. (NEUT t \ NORMAL t) \t\RED \" lemma CR3_implies_CR4[intro]: "CR3 \ \ CR4 \" by (auto simp add: CR3_def CR3_RED_def CR4_def NORMAL_def) lemma%invisible sub_induction: assumes a: "SN(u)" and b: "u\RED \" and c1: "NEUT t" and c2: "CR2 \" and c3: "CR3 \" and c4: "CR3_RED t (\\\)" shows "(App t u)\RED \" using a b proof(induct) fix u assume as: "u\RED \" assume ih: " \u'. \u \ u'; u' \ RED \\ \ App t u' \ RED \" have "NEUT (App t u)" using c1 by (auto simp add: NEUT_def) moreover have "CR3_RED (App t u) \" unfolding CR3_RED_def proof (intro strip) fix r assume red: "App t u \ r" moreover { assume "\t'. t \ t' \ r = App t' u" then obtain t' where a1: "t \ t'" and a2: "r = App t' u" by blast have "t'\RED (\\\)" using c4 a1 by (simp add: CR3_RED_def) then have "App t' u\RED \" using as by simp then have "r\RED \" using a2 by simp } moreover { assume "\u'. u \ u' \ r = App t u'" then obtain u' where b1: "u \ u'" and b2: "r = App t u'" by blast have "u'\RED \" using as b1 c2 by (auto simp add: CR2_def) with ih have "App t u' \ RED \" using b1 by simp then have "r\RED \" using b2 by simp } moreover { assume "\x t'. t = \ x .t'" then obtain x t' where "t = \ x .t'" by blast then have "NEUT (\ x .t')" using c1 by simp then have "False" by (simp add: NEUT_def) then have "r\RED \" by simp } ultimately show "r \ RED \" by (cases) (auto) qed ultimately show "App t u \ RED \" using c3 by (simp add: CR3_def) qed subsection {* Strong normalization for subterms and stacks *} text {* To prove \isa{CR1-3} for the type constructor $\to$, we need a way to obtain @{term "SN s"} from @{term "SN (App s t)"}. This can not be defined using a simple function, since HOL is a logic of total functions and we only want to project to the first element of an application. One could use a function from @{typ "trm"} to @{typ "trm option"} but this would not generalize to the case of dismantling below. Thus, we define a one case inductive relation between terms establishing the desired connection of @{term "App s t"} and @{term "s"}. *} inductive FST :: "trm\trm\bool" (" _ \ _" [80,80] 80) where fst[intro!]: "(App t s) \ t" lemma SN_of_FST_of_App: assumes a: "SN (App t s)" shows "SN t" proof - from a have "\z. (App t s \ z) \ SN z" by (induct rule: SN.induct) (blast elim: FST.cases intro: SN_intro) then show "SN t" by blast qed text {* This lemma is a simplified version of the one used in \cite{SN.thy}. Since we have generalized our notion of reduction from terms to stacks, we can also generalize the notion of strong normalization. The new induction principle will be used to prove the @{term "T"} case of the properties of the reducibility relation. *} inductive SSN :: "stack \ bool" where SSN_intro: "(\ k' . k \ k' \ SSN k') \ SSN k" text {* Furthermore, the approach for deriving strong normalization of subterms from above can be generalized to terms of the form @{term "t \ k"}. In contrast to the case of applications, @{term "t \ k"} does \textit{not} uniquely determine @{term t} and @{term k}. Thus, the extraction is a proper relation in this case. *} inductive SND_DIS :: "trm \ stack \ bool" ("_ \ _") where snd_dis[intro!]: "t \ k \ k" text {* Lemmas like \isa{SN-of-FST-of-App} are usually not proven at all or proven by contradiction -- using the fact that any infinite sequence in a subterm implies an infinite sequence in the whole term. For this reason, the inductive proof below is shown in length, although it could have been proven using automated reasoning tools similar to the case above. *} lemma SN_SSN: assumes a: "SN (t \ k)" shows " SSN k" proof - from a have "\z. (t \ k \ z) \ SSN z" (* by (induct rule: SN.induct) (metis SND_DIS.cases SSN_intro snd_dis stack_reduction_def) *) proof (induct rule: SN.induct) case (SN_intro u) have ih: "\u'. u \ u' \ \z. u' \ z \ SSN z" by fact show "\z. u \ z \ SSN z" proof (intro allI impI) fix z assume "u \ z" thus "SSN z" proof (cases rule:SND_DIS.cases) case (snd_dis v _) hence u: "u = v \ z" by simp { fix z' assume "z \ z'" with u have "u \ v \ z'" by (simp add: stack_reduction_def) hence "\z. (v \ z') \ z \ SSN z" by (rule ih) with u have "SSN z'" by blast } thus "SSN z" .. qed qed qed thus "SSN k" by blast qed subsection {* A new case construct on the reducts of \texorpdfstring{@{term "t \ k"}}{t * k} *} text {* To prove the properties of the logical relation, the authors of \cite{TT-lifting} use a case distinction on the reducts of @{term "t \ k"}, where $t$ is a neutral term and therefore no interaction occurs between $t$ and $k$. %auto generated stuff \begin{isamarkuptext}% \renewcommand\isacharquery{} $$\inferrule{ \isa{{\isacharquery}t\ {\isasymstar}\ {\isacharquery}k\ {\isasymmapsto}\ {\isacharquery}r}\\ \isa{{\isasymAnd}t{\isacharprime}{\isachardot}\ {\isasymlbrakk}{\isacharquery}t\ {\isasymmapsto}\ t{\isacharprime}{\isacharsemicolon}\ {\isacharquery}r\ {\isacharequal}\ t{\isacharprime}\ {\isasymstar}\ {\isacharquery}k{\isasymrbrakk}\ {\isasymLongrightarrow}\ {\isacharquery}P}\\ \isa{NEUT\ {\isacharquery}t}\\ \isa{{\isasymAnd}k{\isacharprime}{\isachardot}\ {\isasymlbrakk}\ {\isacharquery}k\ {\isasymmapsto}\ k{\isacharprime}\ {\isacharsemicolon}\ {\isacharquery}r\ {\isacharequal}\ {\isacharquery}t\ {\isasymstar}\ k{\isacharprime}{\isasymrbrakk}\ {\isasymLongrightarrow}\ {\isacharquery}P}\\ }{ \isa{{\isacharquery}P}}$$% \end{isamarkuptext}% *} (* Unfortunately, here we reach one of the limitations of the Nominal package. While it is possible to chose the binders when doing a case analysis on $t \mapsto t'$ such that they match those occurring within $t$ and $t'$, these binders need to be chosen at the point where one begins the case analysis. However, to establish the case analysis we are striving for, in some of the cases we need to establish stack reductions. By the definition of stack reductions this involves introducing arbitrary terms and showing the reduction at the term level. Here we can \textit{not} assume that all binders are fresh for these new terms as they need to be chosen arbitrarily \textit{after} the binders have been Thus for certain reduction rules the freshness conditions needed to be discharged nevertheless. This includes in particular rule \textit{r10} with its nested binding structure. In addition to the reduction rules we also need to discharge the freshness condition of the second case of the dismantling function.*) text {* We strive for a proof of this rule by structural induction on $k$. The general idea of the case where @{term "k = [y]n\l"} is to move the first stack frame into the term $t$ and then apply the induction hypothesis as a case rule. Unfortunately, this term is no longer neutral, so, for the induction to go through, we need to generalize the claim to also include the possible interactions of non-neutral terms and stacks. *} (* old proof lemma dismantle_cases: fixes t :: trm assumes r: "t \ k \ r" and M: "\ t' . \ t \ t' ; r = t' \ k \ \ P" and K: "\ k' . \ k \ k' ; r = t \ k' \ \ P" and E: "\ s y n l .\ y \ l ; y \ s ; t = [s] ; k = [y]n\l ; r = (n[y::=s]) \ l \ \ P" and A: "\ u x v y n l.\ x \ (y,u,n) ; y \ (v,u) ; t = u to x in v ; k = [y]n\l ; r = (u to x in (v to y in n)) \ l \ \ P " shows "P" using assms proof (nominal_induct k avoiding: t r rule:stack.strong_induct) case (St y n L) note yfresh = `y \ t` `y \ r` `y \ L` note IH = St(4) and M = St(6) and K = St(7) and E = St(8) and A = St(9) txt {* \begin{figure}[p] \begin{center} We get the following hypothesis:\\ @{thm[mode=Rule] M} {\sc M}\\ @{thm[mode=Rule] K} {\sc K}\\ @{thm[mode=Rule] E}{\sc E}\\ @{thm[mode=Rule] A} {\sc A}\\ \vspace{2cm} And we get the following (large) induction hypothesis:\\ \ \\ \inferrule{ @{thm_style[mode=Rule] prem1 IH}\\\\ \\\\ @{thm_style[mode=Rule] prem2 IH}\\\\ \\\\ @{thm_style[mode=Rule] prem3 IH}\\\\ \\\\ @{thm_style[mode=Rule] prem4 IH}\\\\ \\\\ @{thm_style[mode=Rule] prem5 IH} }{ @{thm_style concl IH} } \end{center} \caption{Hypothesis for dismantle-cases} \label{fig:dis_case_rules} \end{figure} The hypothesis we get after applying the induction rule on the stack $k$ can be found in Figure \ref{fig:dis_case_rules}. We immediately strive for the induction hypothesis by using it as case rule moving the first stack frame to the left of the $\star$. Hence we get 5 cases corresponding to the premises of the induction hypothesis. *} thus "P" proof (cases rule:IH[where b="t to y in n" and ba="r"]) case (2 r') txt {* If @{term "m to y in n"} makes a step we reason by case distinction on the successors of @{term "m to y in n"}. *} have red: "t to y in n \ r'" and r: " r = r' \ L" by fact+ from yfresh r have y: "y \ t to y in n" "y \ r'" by (auto simp add: abs_fresh) obtain z where z: "z \ y" "z \ r'" "z \ t to y in n" using ex_fresh[of "(y,r',t to y in n)"] by(auto simp add:fresh_prod fresh_atm) from red r show "P" -- "here we need to choose the binders" proof (cases rule:reduction.strong_cases [ where x="y"and xa="y" and xb="y" and xc="y" and xd="y" and xe="y" and xf="y" and xg="z" and y="y"]) case (r6 s t' u) -- "if $m$ makes a step we use assumption M" with y have m: "t \ t'" "r' = t' to y in n" by (auto) moreover have "y \ (L,t')" using m yfresh by (simp add: fresh_prod reduction_preserves_fresh) ultimately show "P" using M[of t'] r by(auto simp add: stack.inject fresh_prod) next case (r7 s u n') -- "if $n$ makes a step we can use K" with y have n: "n \ n'" and r': "r' = t to y in n'" (* apply (auto simp add: abs_fun_eq1) *) by (auto simp add: alpha) { fix u -- "here we need to fix $u$ to obtain a stack reduction" have " u to y in n \ u to y in n'" using n by (rule r7') hence "(u to y in n) \ L \ (u to y in n') \ L" by (rule dismantle_red) hence " u \ St y n L \ u \ St y n' L" by simp -- "Here we need the improved \\textit{dismantle-simp} rule" } hence "St y n L \ St y n' L" unfolding stack_reduction_def by blast moreover have "r = t \ St y n' L" using r r' by simp ultimately show "P" by (rule K) next case (r8 s u) -- "the case of a $\\beta$-reduction is exactly E" with y have hyp: "t = [s]" "y \ s" "r' = n[y::=s]" by(auto simp add: alpha) thus "P" using E[of "y" "L" "s" "n"] yfresh r by auto next case (r9 s) -- "the case of an $\\eta$-reduction is also a stack reduction" with y have n: "n = [Var y]" and r': "r' = t" by(auto simp add: alpha) { fix u have "u to y in n \ u" unfolding n by (rule r9') hence "(u to y in n) \ L \ u \ L " by (rule dismantle_red) hence "u \ St y n L \ u \ L" by simp } hence "St y n L \ L" unfolding stack_reduction_def by blast moreover have "r = t \ L" using r r' by simp ultimately show "P" by (rule K) next case (r10 l s v) -- "the assoc case holds by A" with y z have "t to y in n = (l to z in v) to y in s" "r' = l to z in (v to y in s)" "z \ (y, l, s)" "y \ (v, l)" by auto with A show "P" using yfresh r by (auto simp add: alpha) qed (insert y, auto) -- "no other reductions are possible" next case (3 L') -- "the ``K'' case is a stack reduction" hence L: "L \ L'" and r: "r = (t to y in n) \ L'" by auto { fix s from L have "(s to y in n) \ L \ (s to y in n) \ L'" unfolding stack_reduction_def by rule hence "s \ St y n L \ s \ St y n L'" by simp } hence "St y n L \ St y n L'" unfolding stack_reduction_def by auto moreover from r have "r = t \ St y n L'" by simp ultimately show "P" by (rule K) next case (5 x z s n' v K) -- "the ``A'' case is again a stack reduction" note red = this -- "We get the following equalities" hence "t to y in n = s to x in v" "L = St z n' K" "r = (s to x in v to z in n') \ K" by auto { fix u have " L = St z n' K" by fact from red have u1: " u \ St y n L = ((u to x in v) to z in n') \ K " by(auto intro: arg_cong[where f="\ x . x \ K"]) moreover { from red(1) have " x \ z" " x \ n'" by (auto simp add: fresh_prod) hence "(u to x in v) to z in n' \ u to x in (v to z in n')" by (rule r10') hence "((u to x in v) to z in n') \ K \ (u to x in (v to z in n')) \ K" by (rule dismantle_red) } ultimately have "u \ St y n L \ (u to x in (v to z in n')) \ K" by (simp (no_asm_simp) del:dismantle_simp) hence "u \ St y n L \ u \ St x (v to z in n') K" by simp } hence "St y n L \ St x (v to z in n') K" unfolding stack_reduction_def by simp moreover have "r = t \ (St x (v to z in n') K)" using red by (auto) ultimately show "P" by (rule K) qed (insert St, auto ) qed auto *) lemma dismantle_cases: fixes t :: trm assumes r: "t \ k \ r" and T: "\ t' . \ t \ t' ; r = t' \ k \ \ P" and K: "\ k' . \ k \ k' ; r = t \ k' \ \ P" and B: "\ s y n l .\ t = [s] ; k = [y]n\l ; r = (n[y::=s]) \ l \ \ P" and A: "\ u x v y n l.\ x \ y; x \ n ; t = u to x in v ; k = [y]n\l ; r = (u to x in (v to y in n)) \ l \ \ P " shows "P" using assms proof (nominal_induct k avoiding: t r rule:stack.strong_induct) case (St y n L) note yfresh = `y \ t` `y \ r` `y \ L` note IH = St(4) and T = St(6) and K = St(7) and B = St(8) and A = St(9) txt {* \begin{figure}[p] \begin{center} We can assume the following hypotheses:\\ @{thm[mode=Rule] T} {\sc T}\\[1ex] @{thm[mode=Rule] K} {\sc K}\\[1ex] @{thm[mode=Rule] B }{\sc B}\\[1ex] @{thm[mode=Rule] A} {\sc A}\\[1ex] \vspace{2cm} And we get the following (large) induction hypothesis:\\ \ \\ \mbox{\mprset{flushleft} \setlength\rulewidth{.75\textwidth} \inferrule{ \rwpage{@{thm_style[display] prem1 IH}}\\\\ \rwpage{@{thm_style[display] prem2 IH}}\\\\ \rwpage{@{thm_style[display] prem3 IH}}\\\\ \rwpage{@{thm_style[display] prem4 IH}}\\\\ \rwpage{@{thm_style[display] prem5 IH}} }{ @{thm_style concl IH} }} \end{center} \caption{Hypotheses for dismantle-cases} \label{fig:dis_case_rules} \end{figure} The hypothesis we get from the induction on the stack $k$ can be found in Figure \ref{fig:dis_case_rules}. We immediately strive for the induction hypothesis by using it as case rule moving the first stack frame to the left of the dismantling operator. Hence we get five cases corresponding to the premises of the induction hypothesis. *} thus "P" proof (cases rule:IH[where b="t to y in n" and ba="r"]) case (2 r') have red: "t to y in n \ r'" and r: " r = r' \ L" by fact+ txt {* If @{term "m to y in n"} makes a step we reason by case distinction on the successors of @{term "m to y in n"}. We want to use the strong inversion principle for the reduction relation. For this we need that $y$ is fresh for @{term "t to y in n"} and $r'$. *} from yfresh r have y: "y \ t to y in n" "y \ r'" by (auto simp add: abs_fresh) obtain z where z: "z \ y" "z \ r'" "z \ t to y in n" using ex_fresh[of "(y,r',t to y in n)"] by(auto simp add:fresh_prod fresh_atm) from red r show "P" proof (cases rule:reduction.strong_cases [ where x="y"and xa="y" and xb="y" and xc="y" and xd="y" and xe="y" and xf="y" and xg="z" and y="y"]) case (r6 s t' u) -- "if $t$ makes a step we use assumption T" with y have m: "t \ t'" "r' = t' to y in n" by auto thus "P" using T[of t'] r by auto next case (r7 _ _ n') with y have n: "n \ n'" and r': "r' = t to y in n'" by (auto simp add: alpha) txt {* Since @{term "k = [y]n\L"}, the reduction @{thm n} occurs within the stack $k$. Hence, we need to establish this stack reduction. *} have "[y]n\L \ [y]n'\L" unfolding stack_reduction_def proof fix u have "u to y in n \ u to y in n'" using n .. hence "(u to y in n) \ L \ (u to y in n') \ L" .. thus " u \ [y]n\L \ u \ [y]n'\L" by simp qed moreover have "r = t \ [y]n'\L" using r r' by simp ultimately show "P" by (rule K) next case (r8 s _) -- "the case of a $\\beta$-reduction is exactly B" with y have "t = [s]" "r' = n[y::=s]" by(auto simp add: alpha) thus "P" using B[of "s" "y" "n" "L"] r by auto next case (r9 _) -- "The case of an $\\eta$-reduction is a stack reduction as well." with y have n: "n = [Var y]" and r': "r' = t" by(auto simp add: alpha) { fix u have "u to y in n \ u" unfolding n .. hence "(u to y in n) \ L \ u \ L " .. hence "u \ [y]n\L \ u \ L" by simp } hence "[y]n\L \ L" unfolding stack_reduction_def .. moreover have "r = t \ L" using r r' by simp ultimately show "P" by (rule K) next case (r10 u _ v) -- "The assoc case holds by A." with y z have "t = (u to z in v)" "r' = u to z in (v to y in n)" "z \ (y,n)" by (auto simp add: fresh_prod alpha) thus "P" using A[of z y n] r by auto qed (insert y, auto) -- "No other reductions are possible." next txt {* Next we have to solve the case where a reduction occurs deep within $L$. We get a reduction of the stack $k$ by moving the first stack frame ``[y]n'' back to the right hand side of the dismantling operator. *} case (3 L') hence L: "L \ L'" and r: "r = (t to y in n) \ L'" by auto { fix s from L have "(s to y in n) \ L \ (s to y in n) \ L'" unfolding stack_reduction_def .. hence "s \ [y]n\L \ s \ [y]n\L'" by simp } hence "[y]n\L \ [y]n\L'" unfolding stack_reduction_def by auto moreover from r have "r = t \ [y]n\L'" by simp ultimately show "P" by (rule K) next case (5 x z n' s v K) -- "The ``assoc'' case is again a stack reduction " have xf: "x \ z" "x \ n'" -- "We get the following equalities" and red: "t to y in n = s to x in v" "L = [z]n'\K" "r = (s to x in v to z in n') \ K" by fact+ { fix u from red have "u \ [y]n\L = ((u to x in v) to z in n') \ K " by(auto intro: arg_cong[where f="\ x . x \ K"]) moreover { from xf have "(u to x in v) to z in n' \ u to x in (v to z in n')" .. hence "((u to x in v) to z in n') \ K \ (u to x in (v to z in n')) \ K" by rule } ultimately have "u \ [y]n\L \ (u to x in (v to z in n')) \ K" by (simp (no_asm_simp) del:dismantle_simp) hence "u \ [y]n\L \ u \ [x](v to z in n')\K" by simp } hence "[y]n\L \ [x](v to z in n')\ K" unfolding stack_reduction_def by simp moreover have "r = t \ ([x](v to z in n')\K)" using red by (auto) ultimately show "P" by (rule K) qed (insert St, auto ) qed auto text {* Now that we have established the general claim, we can restrict $t$ to neutral terms only and drop the cases dealing with possible interactions. *} lemma dismantle_cases'[consumes 2, case_names T K]: fixes m :: trm assumes r: "t \ k \ r" and "NEUT t" and "\ t' . \ t \ t' ; r = t' \ k \ \ P" and "\ k' . \ k \ k' ; r = t \ k' \ \ P" shows "P" using assms unfolding NEUT_def by (cases rule: dismantle_cases[of t k r]) (auto) (*text{* \inferrule{ @{thm_style prem1 "dismantle_cases'"}\\ @{thm_style prem3 "dismantle_cases'"}\\ @{thm_style prem2 "dismantle_cases'"}\\ @{thm_style prem4 "dismantle_cases'"}\\ }{ @{thm_style concl "dismantle_cases'"}} *} *) subsection {* Proof of the properties of reducibility *} text {* Now we are only two simple lemmas away from proving the properties of the reducibility relation.*} lemma red_Ret: fixes t :: trm assumes "[s] \ t" shows " \ s' . t = [s'] \ s \ s'" using assms by cases (auto) lemma SN_Ret: "SN u \ SN [u]" by(induct rule:SN.induct) (metis SN.intros red_Ret) text {* All the properties of reducibility are shown simultaneously by induction on the type. Lindley and Stark \cite{TT-lifting} only spell out the cases dealing with the monadic type constructor $T$. We do the same by reusing the proofs from \cite{SN.thy} for the other cases. To shorten the presentation, these proofs are folded as $\langle\mathit{Urban}\rangle$. *} lemma RED_props: shows "CR1 \" and "CR2 \" and "CR3 \" proof (nominal_induct \ rule: ty.strong_induct) case TBase {%Urban case 1 show "CR1 TBase" by (simp add: CR1_def) next case 2 show "CR2 TBase" by (auto intro: SN_preserved simp add: CR2_def) next case 3 show "CR3 TBase" by (auto intro: SN_intro simp add: CR3_def CR3_RED_def) } next case (TFun \1 \2) {%Urban case 1 have ih_CR3_\1: "CR3 \1" by fact have ih_CR1_\2: "CR1 \2" by fact have "\t. t \ RED (\1 \ \2) \ SN t" proof - fix t assume "t \ RED (\1 \ \2)" then have a: "\u. u \ RED \1 \ App t u \ RED \2" by simp from ih_CR3_\1 have "CR4 \1" by (simp add: CR3_implies_CR4) moreover fix a have "NEUT (Var a)" by (force simp add: NEUT_def) moreover have "NORMAL (Var a)" by (rule normal_var) ultimately have "(Var a)\ RED \1" by (simp add: CR4_def) with a have "App t (Var a) \ RED \2" by simp hence "SN (App t (Var a))" using ih_CR1_\2 by (simp add: CR1_def) thus "SN t" by (auto dest: SN_of_FST_of_App) qed then show "CR1 (\1 \ \2)" unfolding CR1_def by simp next case 2 have ih_CR2_\2: "CR2 \2" by fact then show "CR2 (\1 \ \2)" unfolding CR2_def by auto next case 3 have ih_CR1_\1: "CR1 \1" by fact have ih_CR2_\1: "CR2 \1" by fact have ih_CR3_\2: "CR3 \2" by fact show "CR3 (\1 \ \2)" unfolding CR3_def proof (simp, intro strip) fix t u assume a1: "u \ RED \1" assume a2: "NEUT t \ CR3_RED t (\1 \ \2)" have "SN(u)" using a1 ih_CR1_\1 by (simp add: CR1_def) then show "(App t u)\RED \2" using ih_CR2_\1 ih_CR3_\2 a1 a2 by (blast intro: sub_induction) qed } next case (T \) { case 1 -- {* follows from the fact that @{term "Id \ SRED \"} *} have ih_CR1_\: "CR1 \" by fact { fix t assume t_red: "t \ RED (T \)" { fix s assume "s \ RED \" hence "SN s" using ih_CR1_\ by (auto simp add: CR1_def) hence "SN ([s])" by (rule SN_Ret) hence "SN ([s] \ Id)" by simp } hence "Id \ SRED \" by simp with t_red have "SN (t)" by (auto simp del: SRED.simps) } thus "CR1 (T \)" unfolding CR1_def by blast next case 2 -- {* follows since \isa{SN} is preserved under redcution *} { fix t t'::trm assume t_red: "t \ RED (T \)" and t_t': "t \ t'" { fix k assume k: "k \ SRED \" with t_red have "SN(t \ k)" by simp moreover from t_t' have "t \ k \ t' \ k" .. ultimately have "SN(t' \ k)" by (rule SN_preserved) } hence "t' \ RED (T \)" by (simp del: SRED.simps) } thus "CR2 (T \)"unfolding CR2_def by blast next case 3 from `CR3 \` have ih_CR4_\ : "CR4 \" .. { fix t assume t'_red: "\ t' . t \ t' \ t' \ RED (T \)" and neut_t: "NEUT t" { fix k assume k_red: "k \ SRED \" fix x have "NEUT (Var x)" unfolding NEUT_def by simp hence "Var x \ RED \" using normal_var ih_CR4_\ by (simp add: CR4_def) hence "SN ([Var x] \ k)" using k_red by simp hence "SSN k" by (rule SN_SSN) then have "SN (t \ k)" using k_red proof (induct k rule:SSN.induct) case (SSN_intro k) have ih : "\k'. \ k \ k' ; k' \ SRED \ \ \ SN (t \ k')" and k_red: "k \ SRED \" by fact+ { fix r assume r: "t \ k \ r" hence "SN r" using neut_t proof (cases rule:dismantle_cases') case (T t') hence t_t': "t \ t'" and r_def: "r = t' \ k" . from t_t' have "t' \ RED (T \)" by (rule t'_red) thus "SN r" using k_red r_def by simp next case (K k') hence k_k': "k \ k'" and r_def: "r = t \ k'" . { fix s assume "s \ RED \" hence "SN ([s] \ k)" using k_red by simp moreover have "[s] \ k \ [s] \ k'" using k_k' unfolding stack_reduction_def .. ultimately have "SN ([s] \ k')" .. } hence "k' \ SRED \" by simp with k_k' show "SN r" unfolding r_def by (rule ih) qed } thus "SN (t \ k)" .. qed } hence "t \ RED (T \)" by simp } thus "CR3 (T \)" unfolding CR3_def CR3_RED_def by blast } qed text {* The last case above shows that, once all the reasoning principles have been established, some proofs have a formalization which is amazingly close to the informal version. For a direct comparison, the informal proof is presented in Figure~\ref{fig:cr3}. *} text_raw {* \input{figureCR3} *} text {* Now that we have established the properties of the reducibility relation, we need to show that reducibility is preserved by the various term constructors. The only nontrivial cases are abstraction and sequencing. *} section {* Abstraction Preserves Reducibility *} text {* Once again we could reuse the proofs from \cite{SN.thy}. The proof uses the \isa{double-SN} rule and the lemma \isa{red-Lam} below. Unfortunately, this time the proofs are not fully identical to the proofs in \cite{SN.thy} because we consider $\beta\eta$-reduction rather than $\beta$-reduction. The cases for $\eta$-reductions had to be to ``patched in'', mainly by changing the \isa{red-Lam} lemma accordingly, but some minor adjustments also had to be made to the \isa{abs{\isacharunderscore}RED} lemma. *} lemma%invisible double_SN_aux: assumes a: "SN a" and b: "SN b" and hyp: "\x z. \\y. x \ y \ SN y; \y. x \ y \ P y z; \u. z \ u \ SN u; \u. z \ u \ P x u\ \ P x z" shows "P a b" proof - from a have r: "\b. SN b \ P a b" proof (induct a rule: SN.induct) case (SN_intro x) note SNI' = SN_intro have "SN b" by fact thus ?case proof (induct b rule: SN.induct) case (SN_intro y) show ?case apply (rule hyp) apply (erule SNI') apply (erule SNI') apply (rule SN.SN_intro) apply (erule SN_intro)+ done qed qed from b show ?thesis by (rule r) qed lemma double_SN[consumes 2]: assumes a: "SN a" and b: "SN b" and c: "\(x::trm) (z::trm). \\y. x \ y \ P y z; \u. z \ u \ P x u\ \ P x z" shows "P a b" using a b c by%Urban (blast intro: double_SN_aux) lemma red_Lam: assumes a: "\ x . t \ r" shows " (\t'. r = \ x . t' \ t \ t') \ (t = App r (Var x) \ x \ r ) " proof - obtain z::name where z: "z \ x" "z \ t" "z \ r" using ex_fresh[of "(x,t,r)"] by (auto simp add: fresh_prod) have "x \ \ x . t" by (simp add: abs_fresh) with a have "x \ r" by (simp add: reduction_fresh) with a show ?thesis using z by(cases rule: reduction.strong_cases [where x ="x" and xa="x" and xb="x" and xc="x" and xd="x" and xe="x" and xf="x" and xg="x" and y="z"]) (auto simp add: abs_fresh alpha fresh_atm) qed lemma abs_RED: assumes asm: "\s\RED \. t[x::=s]\RED \" shows "\ x . t \RED (\\\)" proof %Urban - have b1: "SN t" proof - have "Var x\RED \" proof - have "CR4 \" by (simp add: RED_props CR3_implies_CR4) moreover have "NEUT (Var x)" by (auto simp add: NEUT_def) moreover have "NORMAL (Var x)" by (rule normal_var) ultimately show "Var x\RED \" by (simp add: CR4_def) qed then have "t[x::=Var x]\RED \" using asm by simp then have "t\RED \" by (simp add: id_subs) moreover have "CR1 \" by (simp add: RED_props) ultimately show "SN t" by (simp add: CR1_def) qed show "\ x .t\RED (\\\)" proof (simp, intro strip) fix u assume b2: "u\RED \" then have b3: "SN u" using RED_props by (auto simp add: CR1_def) show "App (\ x .t) u \ RED \" using b1 b3 b2 asm proof(induct t u rule: double_SN) fix t u assume ih1: "\t'. \t \ t'; u\RED \; \s\RED \. t'[x::=s]\RED \\ \ App (\ x .t') u \ RED \" assume ih2: "\u'. \u \ u'; u'\RED \; \s\RED \. t[x::=s]\RED \\ \ App (\ x .t) u' \ RED \" assume as1: "u \ RED \" assume as2: "\s\RED \. t[x::=s]\RED \" have "CR3_RED (App (\ x. t) u) \" unfolding CR3_RED_def proof(intro strip) fix r assume red: "App (\ x .t) u \ r" moreover { assume "\t'. t \ t' \ r = App (\ x . t') u" then obtain t' where a1: "t \ t'" and a2: "r = App (\ x .t') u" by blast have "App (\ x .t') u\RED \" using ih1 a1 as1 as2 apply(auto) apply(drule_tac x="t'" in meta_spec) apply(simp) apply(drule meta_mp) prefer 2 apply(auto)[1] apply(rule ballI) apply(drule_tac x="s" in bspec) apply(simp) apply(subgoal_tac "CR2 \")(*A*) apply(unfold CR2_def)[1] apply(drule_tac x="t[x::=s]" in spec) apply(drule_tac x="t'[x::=s]" in spec) apply(simp add: reduction_subst) (*A*) apply(simp add: RED_props) done then have "r\RED \" using a2 by simp } note rt = this moreover { assume "\u'. u \ u' \ r = App (\ x . t) u'" then obtain u' where b1: "u \ u'" and b2: "r = App (\ x .t) u'" by blast have "App (\ x .t) u'\RED \" using ih2 b1 as1 as2 apply(auto) apply(drule_tac x="u'" in meta_spec) apply(simp) apply(drule meta_mp) apply(subgoal_tac "CR2 \") apply(unfold CR2_def)[1] apply(drule_tac x="u" in spec) apply(drule_tac x="u'" in spec) apply(simp) apply(simp add: RED_props) apply(simp) done then have "r\RED \" using b2 by simp } note ru= this moreover { assume "r = t[x::=u]" then have "r\RED \" using as1 as2 by auto } note r = this ultimately show "r \ RED \" (* one wants to use the strong elimination principle; for this one has to know that x\u *) apply(cases) apply(auto) apply(drule red_Lam) apply(drule disjE)prefer 3 apply simp apply(auto)[1] prefer 2 apply(auto simp add: alpha subst_rename')[1] apply(subgoal_tac "App s' u = t[x::=u]") apply(auto) apply(auto simp add: forget) done qed moreover have "NEUT (App (\ x . t) u)" unfolding NEUT_def by (auto) ultimately show "App (\ x . t) u \ RED \" using RED_props by (simp add: CR3_def) qed qed qed section {* Sequencing Preserves Reducibility *} text {* This section corresponds to the main part of the paper being formalized and as such deserves special attention. In the lambda case one has to formalize doing induction on $\max(s) + max(t)$ for two strongly normalizing terms $s$ and $t$ (cf. \cite[Section 6.3]{proofs+types}). Above, this was done through a \isa{double-SN} rule. The central Lemma 7 of Lindley and Stark's paper uses an even more complicated induction scheme. They assume terms $p$ and $n$ as well as a stack $K$ such that @{term "SN(p)"} and @{term "SN(n[x::=p] \ K)"}. The induction is then done on $|K| + max(n \star K) + max(p)$. See Figure~\ref{fig:lemma7} in for details. *} text_raw {* \input{figureLemma7} *} subsection {* Triple induction principle *} text {* Since we have settled for a different characterization of strong normalization, we have to derive an induction principle similar in spirit to the \isa{double-SN} rule. There are, however, some complications. To prove the triple induct principle one needs to keep the variables that the induction is performed on \textit{independent} from one another. Hence the double occurrence of $K$ in the sum above needs to be handled by a suitable abstraction when instantiating the rule. Furthermore, it turns out that it is not necessary to formalize the fact that stack reductions do not increase the length of the stack.\footnote{This possibility was only discovered \textit{after} having formalized $ K \mapsto K' \Rightarrow |K| \geq |K'|$. The proof of this seemingly simple fact was about 90 lines of Isar code.} Doing induction on the sum above, this is necessary to handle the case of a reduction occurring in $K$. We differ from \cite{TT-lifting} and establish an induction principle which to some extent resembles the lexicographic order on $$(\SN,\mapsto) \times (\SN,\mapsto) \times (\N,>)\,.$$ A direct translation would correspond to the lemma: *} lemma triple_induct'[consumes 2]: assumes a: "SN p" and b: "SN (q)" and hyp: "\ (p\trm) (q\trm) (K\stack) . \ \ p' q K. \ SN q; p \ p' \ \ P p' q K ; \ q' K . q \ q' \ P p q' K; \ K' . |K'| < |K| \ P p q K' \ \ P p q K " shows "P p q K" oops (*proof invisible - from a have "SN q \ P p q K" proof (induct p arbitrary: q K) case (SN_intro p q K) have sn1: "\ p' q K . \p \ p'; SN q\ \ P p' q K" by fact have sn_q: "SN q" "SN q" by fact+ thus "P p q K" proof (induct q arbitrary: K) case (SN_intro q K) have sn2: "\ q' K . \q \ q'; SN q'\ \ P p q' K" by fact have "SN q" by fact thus "P p q K" proof (induct K rule: measure_induct_rule[where f="length"]) case (less k) hence le: "\ k' . |k'| < |k| \ P p q k'" by simp { fix p' q k assume "SN q" "p \ p'" hence "P p' q k" using sn1 by auto } moreover { fix q' K assume r: "q \ q'" have "SN q" by fact hence "SN q'" using r by (rule SN_preserved) with r have "P p q' K" using sn2 by auto } ultimately show ?case using le by (auto intro:hyp) qed qed qed with b show ?thesis by blast qed *) text {* The rule derived this was is, however, more general (and hence more difficult to instantiate) than the one we need. Thus, we use the variation below, in which the first hypothesis is only applicable to the original $q$ and $k$, with the benefit that we can drop the extra premise @{term "SN q"}. *} (* text {* Also note, that in the second part of the induction hypothesis the rule is generalized to arbitrary $K$. This means that the rule does not exactly resemble induction on the sum of three integers but more or less the lexicographic order of $(max(p),max(@{term "n[x::=p] \ K"}),|K|)$ *} This has the very interesting side effect that we did \textit{not} have to formalize the fact that stack reductions do not increase the length of the stack.\footnote{This was only recognized \textit{after} having formalized $ K \mapsto K' \Rightarrow length\ K \geq length\ K'$. The proof of this seemingly simple fact was about 90 lines of Isar code} This simplification obviously also applies to the pen and paper proof and would allow to remove the informal argument in the paper where the authors avoid a lengthy inductive proof though ``dot notation''. *) lemma triple_induct[consumes 2]: assumes a: "SN (p)" and b: "SN (q)" and hyp: "\ (p\trm) (q\trm) (k\stack) . \ \ p' . p \ p' \ P p' q k ; \ q' k . q \ q' \ P p q' k; \ k' . |k'| < |k| \ P p q k' \ \ P p q k " shows "P p q k" proof - from a have "\q K . SN q \ P p q K" proof (induct p) case (SN_intro p) have sn1: "\ p' q K . \p \ p'; SN q\ \ P p' q K" by fact have sn_q: "SN q" "SN q" by fact+ thus "P p q K" proof (induct q arbitrary: K) case (SN_intro q K) have sn2: "\ q' K . \q \ q'; SN q'\ \ P p q' K" by fact show "P p q K" proof (induct K rule: measure_induct_rule[where f="length"]) case (less k) have le: "\ k' . |k'| < |k| \ P p q k'" by fact { fix p' assume "p \ p'" moreover have "SN q" by fact ultimately have "P p' q k" using sn1 by auto } moreover { fix q' K assume r: "q \ q'" have "SN q" by fact hence "SN q'" using r by (rule SN_preserved) with r have "P p q' K" using sn2 by auto } ultimately show ?case using le by (auto intro:hyp) qed qed qed with b show ?thesis by blast qed subsection {* Strengthening of the dismantle case rule *} text {* Here we strengthen the case rule for terms of the form @{term "t \ k \ r"}. This is similar to the nominal inversion rules described in Section~\ref{sec:rule-induction}. The freshness requirements on $x$,$y$, and $z$ correspond to those for the rule \isa{reduction.strong-cases}, the strong inversion principle for the reduction relation. \newpage *} lemma dismantle_strong_cases: fixes t :: trm assumes r: "t \ k \ r" and f: "y \ (t,k,r)" "x \ (z,t,k,r)" "z \ (t,k,r)" and T: "\ t' . \ t \ t' ; r = t' \ k \ \ P" and K: "\ k' . \ k \ k' ; r = t \ k' \ \ P" and B: "\ s n l . \ t = [s] ; k = [y]n\l ; r = (n[y::=s]) \ l \ \ P " and A: "\ u v n l . \ x \ (z,n); t = u to x in v ; k = [z]n\l ; r = (u to x in (v to z in n)) \ l \ \ P " shows "P" proof (cases rule:dismantle_cases[of t k r P]) case (4 s y' n L) have ch: "t = [s]" "k = [y']n\L" "r = n[y'::=s] \ L" by fact+ txt {* The equations we get look almost like those we need to instantiate the hypothesis \isa{B}. The only difference is that \isa{B} only applies to $y$, and since we want $y$ to become an instantiation variable of the strengthened rule, we only know that $y$ satisfies \isa{f} and nothing else. But the condition \isa{f} is just strong enough to rename $y'$ to $y$ and apply \isa{B}. *} with f have "y = y' \ y \ n" by (auto simp add: fresh_prod abs_fresh) hence "n[y'::=s] = ([(y,y')] \ n)[y::=s]" and "[y']n\L = [y]([(y,y')] \ n)\L" by(auto simp add: name_swap_bij subst_rename' stack.inject alpha' ) with ch have "t = [s]" "k = [y]([(y,y')] \ n)\L" "r = ([(y,y')] \ n)[y::=s] \ L" by (auto) thus "P" by (rule B) next case (5 u x' v z' n L) have ch: "x' \ z'" "x' \ n" "t = u to x' in v" "k = [z']n\L" "r = (u to x' in v to z' in n) \ L" by fact+ txt {* We want to do the same trick as above but at this point we have to take care of the possibility that $x$ might coincide with $x'$ or $z'$. Similarly, $z$ might coincide with $z'$. *} with f have x: "x = x' \ x \ v to z' in n" and z: "z = z' \ z \ n" by (auto simp add: fresh_prod abs_fresh) from f ch have x': "x' \ n" " x' \ z'" and xz': "x = z' \ x \ n" by (auto simp add:name_swap_bij alpha fresh_prod fresh_atm abs_fresh) from f ch have "x \ z" "x \ [z'].n" by (auto simp add: fresh_prod) with xz' z have " x \ (z , ([(z, z')] \ n))" by (auto simp add: fresh_atm fresh_bij name_swap_bij fresh_prod abs_fresh calc_atm fresh_aux fresh_left) moreover from x ch have "t = u to x in ([(x,x')] \ v)" by (auto simp add:name_swap_bij alpha') moreover from z ch have "k = [z]([(z,z')] \ n)\L" by (auto simp add:name_swap_bij stack.inject alpha') txt {* The first two $\alpha$-renamings are simple, but here we have to handle the nested binding structure of the assoc rule. Since $x$ scopes over the whole term @{term "(v to z' in n)" }, we have to push the swapping over $z'$ *} moreover { from x have "u to x' in (v to z' in n) = u to x in ([(x,x')] \ (v to z' in n))" by (auto simp add:name_swap_bij alpha' simp del: trm.perm) also from xz' x' have "\ = u to x in (([(x,x')] \ v) to z' in n)" by (auto simp add: abs_fun_eq1 swap_simps alpha'') (metis alpha'' fresh_atm perm_fresh_fresh swap_simps(1) x') also from z have " \ = u to x in (([(x,x')] \ v) to z in ([(z,z')] \ n))" by (auto simp add: abs_fun_eq1 alpha' name_swap_bij ) finally have "r = (u to x in (([(x, x')] \ v) to z in ([(z, z')] \ n))) \ L" using ch by (simp del: trm.inject) } ultimately show "P" by (rule A[where n="[(z, z')] \ n" and v="([(x, x')] \ v)"]) qed (insert r T K, auto) subsection {* Strong normalization and substitution *} text {* The lemma in Figure~\ref{fig:lemma7} assumes @{term "SN(n[x::=p] \ K)"} but the actual induction in done on @{term "SN(n \ K)"}. The stronger assumption @{term "SN (n[x::=p] \ K)"} is needed to handle the $\beta$ and $\eta$ cases.*} lemma sn_forget: assumes a: "SN(t[x::=v])" shows "SN t" proof - def dq: q \ "t[x::=v]" from a have "SN q" unfolding dq . thus "SN t" using dq proof (induct q arbitrary: t) case (SN_intro q t) hence ih: "\ t'. \t[x::=v] \ t'[x::=v]\ \ SN t'" by auto { fix t' assume "t \ t'" hence "t[x::=v] \ t'[x::=v]" by (rule reduction_subst) hence "SN t'" by (rule ih) } thus "SN t" .. qed qed lemma sn_forget': assumes sn: "SN (t[x::=p] \ k)" and x: "x \ k" shows "SN (t \ k)" proof - from x have "t[x::=p] \ k = (t \ k)[x::=p]" by (simp add: ssubst_forget) with sn have "SN( (t \ k)[x::=p] )" by simp thus ?thesis by (rule sn_forget) qed abbreviation redrtrans :: "trm \ trm \ bool" (" _ \\<^isup>* _ " ) where "redrtrans \ reduction^**" text {* To be able to handle the case where $p$ makes a step, we need to establish @{term "p \ p' \ (m[x::=p]) \\<^isup>* (m[x::=p'])"} as well as the fact that strong normalization is preserved for an arbitrary number of reduction steps. The first claim involves a number of simple transitivity lemmas. Here we can benefit from having removed the freshness conditions from the reduction relation as this allows all the cases to be proven automatically. Similarly, in the \isa{red-subst} lemma, only those cases where substitution is pushed to two subterms needs to be proven explicitly.*} lemma red_trans: shows r1_trans: " s \\<^isup>* s' \ App s t \\<^isup>* App s' t" and r2_trans: " t \\<^isup>* t' \ App s t \\<^isup>* App s t'" and r4_trans: "t \\<^isup>* t' \ \ x . t \\<^isup>* \ x . t' " and r6_trans: " s \\<^isup>* s' \ s to x in t \\<^isup>* s' to x in t" and r7_trans: "\ t \\<^isup>* t' \ \ s to x in t \\<^isup>* s to x in t'" and r11_trans: "s \\<^isup>* s' \ [s] \\<^isup>* ([s'])" by - (induct rule: rtranclp_induct , (auto intro: transitive_closurep_trans')[2])+ lemma red_subst: "p \ p' \ (m[x::=p]) \\<^isup>* (m[x::=p'])" proof(nominal_induct m avoiding: x p p' rule:trm.strong_induct) case (App s t) hence "App (s[x::=p]) (t[x::=p]) \\<^isup>* App (s[x::=p']) (t[x::=p])" by (auto intro: r1_trans) also from App have "\ \\<^isup>* App (s[x::=p']) ( t[x::=p'])" by (auto intro: r2_trans) finally show ?case by auto next case (To s y n) hence "(s[x::=p]) to y in (n[x::=p]) \\<^isup>* (s[x::=p']) to y in (n[x::=p])" by (auto intro: r6_trans) also from To have " \ \\<^isup>* (s[x::=p']) to y in (n[x::=p']) " by (auto intro: r7_trans) finally show ?case using To by auto qed (auto intro:red_trans) lemma SN_trans : "\ p \\<^isup>* p' ; SN p \ \ SN p' " by (induct rule: rtranclp_induct) (auto intro: SN_preserved) subsection {* Central lemma *} text_raw {* \label{sec:central} *} text {* Now we have everything in place we need to tackle the central ``Lemma 7'' of \cite{TT-lifting}. The proof is quite long, but for the most part, the reasoning is that of \cite{TT-lifting}. *} lemma to_RED_aux: assumes p: "SN p" and x: "x \ p" "x \ k" and npk: " SN (n[x::=p] \ k)" shows "SN (([p] to x in n) \ k)" proof - txt {* The first problem we need to handle is that the triple induction principle, like any induction rule, allows induction only on distinct variables. Hence, we need to introduce a new variable $q$. We later want to instantiate $q$ with @{term "n \ k"}. Furthermore, we need to generalize the claim to arbitrary terms $m$, where @{term "q = m \ k"}. This is needed to handle reductions occuring in $n$. *} { fix q assume "SN q" with p have "\ m . \ q = m \ k ; SN(m[x::=p] \ k) \ \ SN (([p] to x in m) \ k)" using x proof (induct p q rule:triple_induct[where k="k"]) case (1 p q k) -- "We obtain an induction hypothesis for $p$, $q$, and $k$." have ih_p: "\ p' m . \p \ p'; q = m \ k; SN (m[x::=p'] \ k); x \ p'; x \ k\ \ SN (([p'] to x in m) \ k)" by fact have ih_q: "\ q' m k . \q \ q'; q' = m \ k; SN (m[x::=p] \ k); x \ p; x \ k\ \ SN (([p] to x in m) \ k)" by fact have ih_k: "\ k' m . \ |k'| < |k|; q = m \ k'; SN (m[x::=p] \ k'); x \ p; x \ k'\ \ SN (([p] to x in m) \ k')" by fact have q: "q = m \ k" and sn: "SN (m[x::=p] \ k)" by fact+ have xp: "x \ p" and xk: "x \ k" by fact+ txt {* Once again we want to reason via case distinction on the successors of a term including a dismantling operator. Since this time we also need to handle the cases where interactions occur, we want to use the strengthened case rule. We already require $x$ to be suitably fresh. To instantiate the rule, we need another fresh name. *} { fix r assume red: "([p] to x in m) \ k \ r" from xp xk have x1 : "x \ ([p] to x in m) \ k " by (simp add: abs_fresh) with red have x2: "x \ r" by (rule reduction_fresh) obtain z::name where z: "z \ (x,p,m,k,r)" using ex_fresh[of "(x,p,m,k,r)"] by (auto simp add: fresh_prod) have "SN r" proof (cases rule:dismantle_strong_cases [of "[p] to x in m" k r x x z ]) case (5 r') have r: "r = r' \ k" and r': "[p] to x in m \ r'" by fact+ txt {* To handle the case of a reduction occurring somewhere in @{term "[p] to x in m" }, we need to contract the freshness conditions to this subterm. This allows the use of the strong inversion rule for the reduction relation.*} from x1 x2 r have xl:"(x \ [p] to x in m)" and xr:"x \ r'" by auto from z have zl: "z \ ([p] to x in m)" "x \ z" by (auto simp add: abs_fresh fresh_prod fresh_atm) with r' have zr: "z \ r'" by (blast intro:reduction_fresh) -- {* handle all reductions of @{term "[p] to x in m"} *} from r' show "SN r" proof (cases rule:reduction.strong_cases [where x="x" and xa="x" and xb="x" and xc="x" and xd="x" and xe="x" and xf="x"and xg="x" and y="z"]) txt {* The case where @{term "p \ p'"} is interesting, because it requires reasioning about the reflexive transitive closure of the reduction relation. *} case (r6 s s' t) hence ch: "[p] \ s'" "r' = s' to x in m" using xl xr by (auto) from this obtain p' where s: "s' = [p']" and p : "p \ p'" by (blast dest:red_Ret) from p have "((m\k)[x::=p]) \\<^isup>* ((m\k)[x::=p'])" by (rule red_subst) with xk have "((m[x::=p]) \ k) \\<^isup>* ((m[x::=p']) \ k)" by (simp add: ssubst_forget) hence sn: "SN ((m[x::=p']) \ k)" using sn by (rule SN_trans) from p xp have xp' : "x \ p'" by (rule reduction_fresh) from ch s have rr: "r' = [p'] to x in m" by simp from p q sn xp' xk show "SN r" unfolding r rr by (rule ih_p) next case(r7 s t m') hence "r' = [p] to x in m'" and "m \ m'" using xl xr by (auto simp add: alpha) hence rr: "r' = [p] to x in m'" by simp from q `m \ m'` have "q \ m' \ k" by(simp add: dismantle_red) moreover have "m' \ k = m' \ k" .. -- "a triviality" moreover { from `m \ m'` have "(m[x::=p]) \ k \ (m'[x::=p]) \ k" by (simp add: dismantle_red reduction_subst) with sn have "SN(m'[x::=p] \ k)" .. } ultimately show "SN r" using xp xk unfolding r rr by (rule ih_q) next case (r8 s t) -- {* the $\beta$-case is handled by assumption *} hence "r' = m[x::=p]" using xl xr by(auto simp add: alpha) thus "SN r" unfolding r using sn by simp next case (r9 s) -- {* the $\eta$-case is handled by assumption as well *} hence "m = [Var x]" and "r' = [p]" using xl xr by(auto simp add: alpha) hence "r' = m[x::=p]" by simp thus "SN r" unfolding r using sn by simp qed (simp_all only: xr xl zl zr abs_fresh , auto) -- {* There are no other possible reductions of @{term "[p] to x in m"}. *} next case (6 k') have k: "k \ k'" and r: "r = ([p] to x in m) \ k'" by fact+ from q k have "q \ m \ k'" unfolding stack_reduction_def by blast moreover have "m \ k' = m \ k'" .. moreover { have "SN (m[x::=p] \ k)" by fact moreover have "(m[x::=p]) \ k \ (m[x::=p]) \ k'" using k unfolding stack_reduction_def .. ultimately have "SN (m[x::=p] \ k')" .. } moreover note xp moreover from k xk have "x \ k'" by (rule stack_reduction_fresh) ultimately show "SN r" unfolding r by (rule ih_q) next txt {* The case of an assoc interaction between @{term "[p] to x in m" } and @{term "k"} is easily handled by the induction hypothesis, since @{term "(m[x::=p]) \ k"} remains fixed under assoc. *} case (8 s t u L) hence k: "k = [z]u\L" and r: "r = ([p] to x in (m to z in u)) \ L" and u: "x \ u" by(auto simp add: alpha fresh_prod) let ?k = L and ?m = "m to z in u" from k z have "|?k| < |k|" by (simp add: fresh_prod) moreover have "q = ?m \ ?k" using k q by simp moreover { from k u z xp have "(?m[x::=p] \ ?k) = (m[x::=p]) \ k" by(simp add: fresh_prod forget) hence "SN (?m[x::=p] \ ?k)" using sn by simp } moreover from xp xk k have "x \ p" and "x \ ?k" by auto ultimately show "SN r" unfolding r by (rule ih_k) qed (insert red z x1 x2 xp xk , auto simp add: fresh_prod fresh_atm abs_fresh) } thus "SN (([p] to x in m) \ k)" .. qed } moreover have "SN ((n[x::=p]) \ k)" by fact moreover hence "SN (n \ k)" using `x \ k` by (rule sn_forget') ultimately show ?thesis by blast qed text {* Having established the claim above, we use it show that \textsf{to}-bindings preserve reducibility. *} lemma to_RED: assumes s: "s \ RED (T \)" and t : " \ p \ RED \ . t[x::=p] \ RED (T \)" shows "s to x in t \ RED (T \)" proof - { fix K assume k: "K \ SRED \" { fix p assume p: "p \ RED \" hence snp: "SN p" using RED_props by(simp add: CR1_def) obtain x'::name where x: "x' \ (t, p, K)" using ex_fresh[of "(t,p,K)"] by (auto) from p t k have "SN((t[x::=p]) \ K)" by auto with x have "SN ((([(x',x)] \ t )[x'::=p]) \ K)" by (simp add: fresh_prod subst_rename) with snp x have snx': "SN (([p] to x' in ([(x',x)] \ t )) \ K)" by (auto intro: to_RED_aux) from x have "[p] to x' in ([(x',x)] \ t ) = [p] to x in t" by simp (metis alpha' fresh_prod name_swap_bij x) moreover have "([p] to x in t) \ K = [p] \ [x]t\K" by simp ultimately have snx: "SN([p] \ [x]t\K)" using snx' by (simp del: trm.inject) } hence "[x]t\K \ SRED \" by simp with s have "SN((s to x in t) \ K)" by(auto simp del: SRED.simps) } thus "s to x in t \ RED (T \)" by simp qed section {* Fundamental Theorem *} text {* The remainder of this section follows \cite{SN.thy} very closely. We first establish that all well typed terms are reducible if we substitute reducible terms for the free variables. *} abbreviation mapsto :: "(name\trm) list \ name \ trm \ bool" ("_ maps _ to _" [55,55,55] 55) where "\ maps x to e \ (lookup \ x) = e" abbreviation closes :: "(name\trm) list \ (name\ty) list \ bool" ("_ closes _" [55,55] 55) where "\ closes \ \ \x \. ((x,\) \ set \ \ (\t. \ maps x to t \ t \ RED \))" theorem fundamental_theorem: assumes a: "\ \ t : \" and b: "\ closes \" shows "\ \ RED \" using a b proof(nominal_induct avoiding: \ rule: typing.strong_induct) case (t3 a \ \ t \ \) -- "lambda case" {%Urban have ih: "\\. \ closes ((a,\)#\) \ \ \ RED \" by fact have \_cond: "\ closes \" by fact have fresh: "a\\" "a\\" by fact+ from ih have "\s\RED \. ((a,s)#\) \ RED \" using fresh \_cond fresh_context by simp then have "\s\RED \. \[a::=s] \ RED \" using fresh by (simp add: psubst_subst) then have "\ a . (\) \ RED (\ \ \)" by (simp only: abs_RED) then show "\<(\ a . t)> \ RED (\ \ \)" using fresh by simp } next case (t5 x \ s \ t \ \) -- "to case" have ihs : "\ \ . \ closes \ \ \ \ RED (T \)" by fact have iht : "\ \ . \ closes ((x, \) # \) \ \ \ RED (T \)" by fact have \_cond: "\ closes \" by fact have fresh: "x \ \" "x \ \" "x \ s" by fact+ from ihs have "\ \ RED (T \)" using \_cond by simp moreover { from iht have "\s\RED \. ((x,s)#\) \ RED (T \)" using fresh \_cond fresh_context by simp hence "\s\RED \. \[x::=s] \ RED (T \)" using fresh by (simp add: psubst_subst) } ultimately have "(\) to x in (\) \ RED (T \)" by (simp only: to_RED) thus "\ \ RED (T \)" using fresh by simp qed auto -- "all other cases are trivial" text {* The final result then follows using the identity substitution, which is $\Gamma$-closing since all variables are reducible at any type. This technique is standard for logical relations proofs. *} fun "id" :: "(name\ty) list \ (name\trm) list" where "id [] = []" | "id ((x,\)#\) = (x,Var x)#(id \)" lemma id_maps: shows "(id \) maps a to (Var a)" by (induct \) (auto) lemma id_fresh: fixes x::"name" assumes x: "x \ \" shows "x \ (id \)" using x by (induct \) (auto simp add: fresh_list_nil fresh_list_cons) lemma id_apply: shows "(id \) = t" by (nominal_induct t avoiding: \ rule: trm.strong_induct) (auto simp add: id_maps id_fresh) lemma id_closes: shows "(id \) closes \" proof - { fix x \ assume "(x,\) \ set \" have "CR4 \" by(simp add: RED_props CR3_implies_CR4) hence "Var x \ RED \" by(auto simp add: NEUT_def normal_var CR4_def) hence "(id \) maps x to Var x \ Var x \ RED \" by (simp add: id_maps) } thus ?thesis by blast qed subsection {* Strong normalization theorem *} lemma typing_implies_RED: assumes a: "\ \ t : \" shows "t \ RED \" proof - have "(id \)\RED \" proof - have "(id \) closes \" by (rule id_closes) with a show ?thesis by (rule fundamental_theorem) qed thus"t \ RED \" by (simp add: id_apply) qed theorem strong_normalization: assumes a: "\ \ t : \" shows "SN(t)" proof - from a have "t \ RED \" by (rule typing_implies_RED) moreover have "CR1 \" by (rule RED_props) ultimately show "SN(t)" by (simp add: CR1_def) qed text {* This finishes our formalization effort. The last theorem corresponds directly to Theorem~\ref{thm:normalization}, the theorem we set out to prove in Chapter~\ref{chap:toptop}. As noted initially, this whole chapter is generated from the Isabelle theory file, which consists of roughly 1500 lines of proof code. The reader is invited to replay some of the more technical proofs using the theory file provided at the authors website.\footnote{\url{http://www.ps.uni-sb.de/\string~doczkal/master/}} A shorter proof script might have been obtained using simple \isacommand{apply} scripts instead of Isar structured proofs, but this would have largely obscured the proof. *} (*<*)end(*>*)