Theory EtaExpansion

theory EtaExpansion
imports Launchbury.Terms Launchbury.Substitution
begin

definition fresh_var :: "exp  var" where
  "fresh_var e = (SOME v. v  fv e)"

lemma fresh_var_not_free:
  "fresh_var e  fv e"
proof-
  obtain v :: var where "atom v  e" by (rule obtain_fresh)
  hence "v  fv e" by (metis fv_not_fresh)
  thus ?thesis unfolding fresh_var_def by (rule someI)
qed

lemma fresh_var_fresh[simp]:
  "atom (fresh_var e)  e"
  by (metis fresh_var_not_free fv_not_fresh)

lemma fresh_var_subst[simp]:
  "e[fresh_var e::=x] = e"
  by (metis fresh_var_fresh subst_fresh_noop)

fun eta_expand :: "nat  exp  exp" where
   "eta_expand 0 e = e"
|  "eta_expand (Suc n) e = (Lam [fresh_var e]. eta_expand n (App e (fresh_var e)))"

lemma eta_expand_eqvt[eqvt]:
  "π  (eta_expand n e) = eta_expand (π  n) (π  e)"
  apply (induction n arbitrary: e π)
  apply (auto simp add: fresh_Pair permute_pure)
  apply (metis fresh_at_base_permI fresh_at_base_permute_iff fresh_var_fresh subst_fresh_noop subst_swap_same)
  done

lemma fresh_eta_expand[simp]: "a  eta_expand n e  a  e"
  apply (induction n arbitrary: e)
  apply  (simp add: fresh_Pair)
  apply  (clarsimp simp add: fresh_Pair fresh_at_base)
  by (metis fresh_var_fresh)

lemma subst_eta_expand: "(eta_expand n e)[x ::= y] = eta_expand n (e[x ::= y])"
proof (induction n arbitrary: e)
case 0 thus ?case by simp
next
case (Suc n)
  obtain z :: var where "atom z  (e, fresh_var e, x, y)" by (rule obtain_fresh)
  
  have "(eta_expand (Suc n) e)[x::=y] = (Lam [fresh_var e]. eta_expand n (App e (fresh_var e)))[x::=y]" by simp
  also have " = (Lam [z]. eta_expand n (App e z))[x::=y]"
    apply (subst change_Lam_Variable[where y' = z])
    using atom z  _
    by (auto simp add: fresh_Pair eta_expand_eqvt pure_fresh permute_pure flip_fresh_fresh intro!: eqvt_fresh_cong2[where f = eta_expand, OF eta_expand_eqvt])
  also have " = Lam [z]. (eta_expand n (App e z))[x::=y]"
    using atom z  _ by simp
  also have " = Lam [z]. eta_expand n (App e z)[x::=y]" unfolding Suc.IH..
  also have " = Lam [z]. eta_expand n (App e[x::=y] z)"
    using atom z  _ by simp
  also have " = Lam [fresh_var (e[x::=y])]. eta_expand n (App e[x::=y] (fresh_var (e[x::=y])))"
    apply (subst change_Lam_Variable[where y' = "fresh_var (e[x::=y])"])
    using atom z  _
    by (auto simp add: fresh_Pair eqvt_fresh_cong2[where f = eta_expand, OF eta_expand_eqvt] pure_fresh eta_expand_eqvt  flip_fresh_fresh subst_pres_fresh simp del: exp_assn.eq_iff)
  also have " = eta_expand (Suc n) e[x::=y]" by simp
  finally show ?case.
qed

lemma isLam_eta_expand:
  "isLam e  isLam (eta_expand n e)" and "n > 0  isLam (eta_expand n e)"
by (induction n) auto

lemma isVal_eta_expand:
  "isVal e  isVal (eta_expand n e)" and "n > 0  isVal (eta_expand n e)"
by (induction n) auto


end