File Abstr.ML


(*
      File: Abstr.ML
    Author: Simon Ambler (sja4@mcs.le.ac.uk)
 Copyright: Leicester University, 2001.
Time-stamp: <2002-04-10 15:04:40 am133>

	Abstract syntax for the Lambda Calculus
*)


Goal "abst i s ==> ! j. i <= j --> abst j s";
be abstSet.induct 1;
by (ALLGOALS (fast_tac (claset()
	addIs abstSet.intrs
	addss simpset())));
qed_spec_mp "abst_le_abst";


Goal "abst 0 e ==> abst i e";
br abst_le_abst 1;
ba 1;
br le0 1;
qed "abst_0_abst";


Goalw [abstr_def] "abstr (% x. (CON n))";
brs abstSet.intrs 1;
qed "abstr_CON";


Goalw [abstr_def] "abstr (% x. x)";
brs abstSet.intrs 1;
qed "abstr_id";


Goalw [abstr_def] "abstr (% x. (VAR n))";
brs abstSet.intrs 1;
qed "abstr_VAR";


Goalw [abstr_def] "[| abstr e; abstr f |] ==> abstr (% x. e x $$ f x)";
brs abstSet.intrs 1;
ba 1;
ba 1;
qed "abstr_APP";


Goal "level i s ==> abst i (% x. s)";
be levelSet.induct 1;
by (ALLGOALS (fast_tac (claset() addIs abstSet.intrs)));
qed "level_abst_const";


Goalw [proper_def, abstr_def] "proper s ==> abstr (% x. s)";
br level_abst_const 1;
ba 1;
qed "proper_abstr_const";


Goal "((% i. e i) = (% j. f j)) = (! k. e k = f k)";
br iffI 1;
be subst 1;
br allI 1;
br refl 1;
br ext 1;
be allE 1;
ba 1;
qed "ext_simp";


val ext_expr_inject = 
  let
	fun prv form = prove_goal thy form
		(fn prs => [fast_tac (claset()
			addss (simpset() addsimps [ext_simp])) 1]);
  in
	map prv [
		"((% x. CON a) = (% x. CON b)) = (a = b)",
		"((% x. VAR j) = (% x. VAR k)) = (j = k)",
		"((% x. BND j) = (% x. BND k)) = (j = k)",
		"((% x. e x $$ g x) = (% y. f y $$ h y)) = (e = f & g = h)",
		"((% x. ABS (e x)) = (% y. ABS (f y))) = (e = f)"
		]
  end;


Goal "(% x :: 'a expr. x) ~= (% x. s)";
by (simp_tac (simpset() addsimps [ext_simp]) 1);
by (res_inst_tac [("x","if VAR 0 = s then VAR 1 else VAR 0")] exI 1);
by (fast_tac (claset()
	addss (simpset() addsplits [split_if_asm])) 1);
qed "id_neq_const";


Goal "(% x :: 'a expr. x) ~= (% x. f x $$ g x)";
by (asm_full_simp_tac (simpset() addsimps [ext_simp]) 1);
auto();
qed "id_neq_APP";


Goal "(% x :: 'a expr. x) ~= (% x. ABS (e x))";
by (asm_full_simp_tac (simpset() addsimps [ext_simp]) 1);
auto();
qed "id_neq_ABS";

Goal " (%x. x) = (%x. VAR n) ==> False";
by (asm_full_simp_tac (simpset() addsimps [ext_simp]) 1);
auto();
qed "id_neq_VAR";

val ext_expr_distinct =
  let
	fun prv form = prove_goal thy form
		(fn prs => [simp_tac (simpset() addsimps [ext_simp]) 1]);

	val expr_distinct = [id_neq_const, id_neq_APP, id_neq_ABS]
		@
		map prv [
		"(% x. CON a) ~= (% x. VAR n)",
		"(% x. CON a) ~= (% x. BND j)",
		"(% x. CON a) ~= (% x. f x $$ g x)",
		"(% x. CON a) ~= (% x. ABS (e x))",
		"(% x. VAR n) ~= (% x. BND j)",
		"(% x. VAR n) ~= (% x. f x $$ g x)",
		"(% x. VAR n) ~= (% x. ABS (e x))",
		"(% x. BND j) ~= (% x. f x $$ g x)",
		"(% x. BND j) ~= (% x. ABS (e x))",
		"(% x. f x $$ g x) ~= (% x. ABS (e x))"
		]
 
  in
	expr_distinct @ (expr_distinct RL [not_sym])

  end;


val ext_simps = ext_expr_inject @ ext_expr_distinct;


val ordinaryIs =
  let
	fun prv form = prove_goalw thy [ordinary_def] form
		(fn prs => [
			simp_tac (simpset() addsimps ext_simps) 1
		])
  in
	map prv [
	"ordinary (% x. CON a)",
	"ordinary (% x. x)",
	"ordinary (% x. VAR n)",
	"ordinary (% x. BND j)",
	"ordinary (% x. f x $$ g x)",
	"ordinary (% x. ABS (e x))"
	]

  end;


Addsimps (ext_simps);


val lbndSet_Es =
  let
	val rbt = rule_by_tactic (ALLGOALS (full_simp_tac
		(simpset() addsimps ordinaryIs)))
  in
	map (rbt o lbndSet.mk_cases) [
		"lbnd i (% x. CON a) s",
		"lbnd i (% x. x) s",
		"lbnd i (% x. VAR n) s",
		"lbnd i (% x. BND j) s",
		"lbnd i (% x. f x $$ g x) s",
		"lbnd i (% x. ABS (e x)) s"
	 ]
  end;


Delsimps (ext_simps);


Goal "lbnd i e s ==> ! t. lbnd i e t --> s = t";
be lbndSet.induct 1;
by (fast_tac (claset()
	addEs [lbndSet.elim]
	addss (simpset() addsimps ordinaryIs)) 7);
by (ALLGOALS (blast_tac (claset() addEs lbndSet_Es)));
qed_spec_mp "lbnd_unique";


Goalw [lbind_def] "lbnd i e s ==> lbind i e = s";
br some_equality 1;
ba 1;
br lbnd_unique 1;
ba 1;
ba 1;
qed "lbnd_lbind";


Goalw [rank_def] "rank f < rank (% x. f x $$ g x)";
by (Simp_tac 1);
qed "rank_APP1";

Goalw [rank_def] "rank g < rank (% x. f x $$ g x)";
by (Simp_tac 1);
qed "rank_APP2";

Goalw [rank_def] "rank e < rank (% x. ABS (e x))";
by (Simp_tac 1);
qed "rank_ABS";


val expr_ranks = [rank_APP1, rank_APP2, rank_ABS];


val prems = Goal "[| !! j. P (% x. VAR j); \
	\     !! a. P (% x. CON a); \
	\           P (% x. x); \
	\     !! j. P (% x. BND j); \
	\   !! f g. [| P f; P g |] ==> P (% x. f x $$ g x); \
	\     !! f. P f ==> P (% x. ABS (f x)); \
	\     !! f. ~ ordinary f ==> P f |] \
			\ ==> P e";
by (res_inst_tac [("P","P"), ("f", "rank")]
	measure_induct 1);
by (case_tac "ordinary x" 1);
brs prems 2;
ba 2;
bw ordinary_def;
by Safe_tac;
by (ALLGOALS (blast_tac (claset()
	addIs prems @ expr_ranks
	addss simpset())));
qed "abstraction_induct";


Goalw [lbind_def] "! i. lbnd i e (lbind i e)";
by (res_inst_tac [("P","% e. ! i. lbnd i e (@ s. lbnd i e s)")]
	abstraction_induct 1);
by (ALLGOALS (rtac allI THEN'
	rtac someI THEN' resolve_tac lbndSet.intrs));
by Auto_tac;
qed_spec_mp "lbnd_existence";


Goal "lbind i (% x. CON a) = CON a";
br lbnd_lbind 1;
brs lbndSet.intrs 1;
qed "lbind_CON";


Goal "lbind i (% x. x) = BND i";
br lbnd_lbind 1;
brs lbndSet.intrs 1;
qed "lbind_id";


Goal "lbind i (% x. VAR n) = VAR n";
br lbnd_lbind 1;
brs lbndSet.intrs 1;
qed "lbind_VAR";


Goal "lbind i (% x. BND j) = BND j";
br lbnd_lbind 1;
brs lbndSet.intrs 1;
qed "lbind_BND";


Goal "lbind i (% x. f x $$ g x) = lbind i f $$ lbind i g";
br lbnd_lbind 1;
brs lbndSet.intrs 1;
br lbnd_existence 1;
br lbnd_existence 1;
qed "lbind_APP";


Goal "lbind i (% x. ABS (e x)) = ABS (lbind (Suc i) e)";
br lbnd_lbind 1;
brs lbndSet.intrs 1;
br lbnd_existence 1;
qed "lbind_ABS";


Goal "~ordinary e ==> lbind i e = BND 0";
br lbnd_lbind 1;
brs lbndSet.intrs 1;
ba 1;
qed "lbind_not_ordinary";


Goal "! i. lbind i (% x. s) = s";
by (induct_tac "s" 1);
by (ALLGOALS (asm_simp_tac (simpset()
	addsimps [lbind_CON, lbind_id, lbind_VAR, lbind_BND,
		lbind_APP, lbind_ABS])));
qed_spec_mp "lbind_const";


val lbind_simps = [lbind_id, lbind_const, lbind_APP, lbind_ABS];


Goal "abst i e ==> ! f. abst i f --> (lbind i e = lbind i f) = (e = f)";
be abstSet.induct 1;
by (ALLGOALS strip_tac);
by (ALLGOALS (
	eres_inst_tac [("aa","(i,fa)")] abstSet.elim
	ORELSE'
	eres_inst_tac [("aa","(i,f)")] abstSet.elim));
by (ALLGOALS (fast_tac (claset()
	addss (simpset() addsimps lbind_simps))));
qed_spec_mp "abst_lbind_simp";


Goalw [abstr_def, lambda_def]
	"[| abstr e; abstr f |] ==> (LAM x. e x = LAM y. f y) = (e = f)";
by (Simp_tac 1);
br abst_lbind_simp 1;
ba 1;
ba 1;
qed "abstr_LAM_simp";


Goal "abst i e ==> level (Suc i) (lbind i e)";
be abstSet.induct 1;
by (ALLGOALS (fast_tac (claset()
	addIs levelSet.intrs
	addss (simpset() addsimps lbind_simps))));
qed "abst_level_lbind";


Goalw [proper_def] "proper s ==> level i s";
br level_0_level 1;
ba 1;
qed "proper_level";


Goalw [abstr_def] "abstr e ==> level i (ABS (lbind i e))";
brs levelSet.intrs 1;
br (abst_0_abst RS abst_level_lbind) 1;
ba 1;
qed "abstr_level";


(* Proper solver --- for goals of the form "proper s" *)


local

  (* Will only resolve against goals of the right form *)
  val proper_filter_thm =
	prove_goal thy "proper s ==> proper s"
		(fn prems => [resolve_tac prems 1]);
in

  fun proper_tac defs prems =
	rtac proper_filter_thm
	THEN'
	simp_tac (simpset()
		addsimps defs @ [proper_def, lambda_def] @ lbind_simps)
	THEN'
	fast_tac (claset()
		addIs levelSet.intrs @ prems
		addEs [proper_level, abstr_level])
end;


fun proper_solver defs = mk_solver "proper"
		(fn prems => proper_tac defs prems);



Goalw [abstr_def] "abstr e ==> abst i e";
br abst_0_abst 1;
ba 1;
qed "abstr_abst";


Goal "level i s ==> ! j. i<=j --> abst j (% x. s)";
be levelSet.induct 1;
by (ALLGOALS (fast_tac (claset() addIs abstSet.intrs
		addss simpset())));
qed_spec_mp "level_abst_const";


Goalw [proper_def] "proper s ==> abst i (% x. s)";
br level_abst_const 1;
br level_0_level 1;
ba 1;
br le_refl 1;
qed "proper_abst";


(* Abstraction solver --- for goals of the form "abstr (% x. e x)" *)


local

  (* Will only resolve against goals of the right form *)
  val abstr_filter_thm =
	prove_goal thy "abstr (% x. e x) ==> abstr (% x. e x)"
		(fn prems => [resolve_tac prems 1]);
in

  fun abstr_tac defs prems =
	rtac abstr_filter_thm
	THEN'
	simp_tac (simpset()
		addsimps defs @ [abstr_def, lambda_def] @ lbind_simps)
	THEN'
	fast_tac (claset()
		addIs abstSet.intrs @ prems
		addEs [abstr_abst, proper_abst])
end;


fun abstr_solver defs = mk_solver "abstr"
		(fn prems => abstr_tac defs prems);


val LAM_distinct =
  let
	fun prv form = prove_goal thy form
		(fn prs => [simp_tac (simpset() addsimps [lambda_def]) 1]);

	val distinct = map prv [
		"LAM x. e x ~= VAR n",
		"LAM x. e x ~= BND j",
		"LAM x. e x ~= s $$ t"
		]
  in
	distinct @ (distinct RL [not_sym])

  end;


Addsimps LAM_distinct;


val ext_LAM_distinct =
  let
	fun prv form = prove_goal thy form
		(fn prs => [simp_tac (simpset()
				addsimps ext_simps @ [lambda_def]) 1]);

	val distinct = map prv [
		"(% x. LAM y. f y x) ~= (% x. VAR n)",
		"(% x. LAM y. f y x) ~= (% x. x)",
		"(% x. LAM y. f y x) ~= (% x. BND j)",
		"(% x. LAM y. f y x) ~= (% x. g x $$ h x)"
		]
  in
	distinct @ (distinct RL [not_sym])

  end;



(* The following two results for `inst' are made obsolete by
   the results for `insts' following. Need multiple instantiation. *)

Goal "! i. s = lbind i (% x. inst i x s)";
by (induct_tac "s" 1);
br allI 3;
by (case_tac "nat=i" 3);
by (ALLGOALS (asm_simp_tac (simpset() addsimps lbind_simps)));
qed_spec_mp "lbind_inst";


Goalw [lambda_def] "ABS s = LAM x. inst 0 x s";
by (simp_tac (simpset() addsimps [lbind_inst]) 1);
qed "ABS_LAM";


Goal "abst i e ==> e = (% x. inst i x (lbind i e))";
br ext 1;
be abstSet.induct 1;
by (ALLGOALS (asm_simp_tac (simpset() addsimps lbind_simps)));
qed "abst_inst_lbind";


Goal "! i. insts (Suc i) xs s = lbind i (% x. insts i (x#xs) s)";
by (induct_tac "s" 1);
by (asm_simp_tac (simpset() addsimps lbind_simps) 1);
by (asm_simp_tac (simpset() addsimps lbind_simps) 1);
br allI 1;
by (case_tac "Suc i <= nat" 1);
by (case_tac "nat" 1);
by (Asm_full_simp_tac 1);
by (asm_simp_tac (simpset() addsimps [Suc_diff_le] @ lbind_simps) 1);
by (case_tac "nat - i < Suc (length xs)" 1);
bd not_leE 1;
by (eres_inst_tac [("m","nat")] less_SucE 1);
by (ALLGOALS (asm_simp_tac (simpset() addsimps lbind_simps)));
qed_spec_mp "insts_lbind";


Goalw [lambda_def, lconv_def] "lconv xs (ABS s) = LAM x. lconv (x # xs) s";
by (simp_tac (simpset() addsimps [insts_lbind]) 1);
qed "lconv_ABS";


val lconv_simps = 
  let
	fun prv form = prove_goalw thy [lconv_def] form
		(fn prs => [simp_tac (simpset() addsimps []) 1]);
  in
	map prv [
		"lconv xs (CON a) = CON a",
		"lconv xs (VAR n) = VAR n",
  		"lconv xs (BND j) = (if j < length xs then xs ! j else BND j)",
		"lconv xs (s $$ t) = lconv xs s $$ lconv xs t"
		]
	@ [lconv_ABS]
  end;


Goal "! i. insts i [] s = s";
by (induct_tac "s" 1);
by Auto_tac;
qed_spec_mp "insts_empty";


Goalw [lconv_def] "lconv [] s = s";
by (simp_tac (simpset() addsimps [insts_empty]) 1);
qed "lconv_id";


(*

To convert HOAS into de Bruijn form:

by (simp_tac (simpset() addsimps [lambda_def] @ lbind_simps) 1);

To convert de Bruijn back into HOAS:

by (res_inst_tac [("P","?P"), ("t","?t")] (lconv_id RS subst) 1);
by (simp_tac (simpset() addsimps lconv_simps) 1);

It helps to choose a suitable instantiation for "P" or "t"
so as to indicate which term is being rewritten.

*)


Goal "abst i e ==> level i (e (VAR n))";
be abstSet.induct 1;
by (ALLGOALS (fast_tac (claset() addIs levelSet.intrs)));
qed "abst_level";


Goalw [dflt_def] "proper dflt";
by (proper_tac [] [] 1);
qed "proper_dflt";


Goalw [dflt_def, lambda_def] "newFor n dflt";
by (simp_tac (simpset() addsimps lbind_simps) 1);
qed "newFor_dflt";


Goalw [dflt_def, lambda_def] "gV dflt = 0";
by (simp_tac (simpset() addsimps lbind_simps) 1);
qed "gV_dflt";

Addsimps [newFor_dflt, gV_dflt];


Goalw [abstr_def]
  "abstr e ==> ! f n. abstr f --> newFor n (e dflt) --> newFor n (f dflt) \
	\ --> e (VAR n) = f (VAR n) --> e = f";
be abstSet.induct 1;
by (ALLGOALS strip_tac);
by (ALLGOALS (
	eres_inst_tac [("aa","(i,fa)")] abstSet.elim
	ORELSE'
	eres_inst_tac [("aa","(i,f)")] abstSet.elim));
by (ALLGOALS (fast_tac (claset()
	addss (simpset() addsimps ext_simps))));
qed_spec_mp "abstr_extensionality";


Goal "[| abstr e; abstr f; ! n. e (VAR n) = f (VAR n) |] ==> e = f";
br abstr_extensionality 1;
ba 1;
ba 1;
by (res_inst_tac [("j","gV (e dflt) + gV (f dflt)")] gV_newFor 1);
by (res_inst_tac [("j","gV (e dflt) + gV (f dflt)")] gV_newFor 2);
by Auto_tac;
qed "abstr_all_extensionality";


Goal "abst i e ==> level 0 t --> level i (e t)";
be abstSet.induct 1;
by (ALLGOALS (fast_tac (claset()
	addIs levelSet.intrs
	addEs [level_0_level])));
qed_spec_mp "abst_level_0_level";


Goalw [proper_def, abstr_def]
  "[| abstr e; proper t |] ==> proper (e t)";
by (rtac abst_level_0_level 1 THEN atac 1 THEN atac 1);
qed "abstr_proper_proper";


Goal "level i s ==> abst i (subst s n)";
be levelSet.induct 1;
by (case_tac "n=na" 2);
by (ALLGOALS (fast_tac (claset()
	addIs abstSet.intrs
	addss simpset())));
qed "level_abst_subst";


Goalw [proper_def, abstr_def] "proper s ==> abstr (subst s n)";
br level_abst_subst 1;
ba 1;
qed "proper_abstr_subst";


Goal "[| e (VAR n) = s; abstr e; newFor n (e dflt) |] ==> subst s n = e";
br abstr_extensionality 1;
by (ALLGOALS (fast_tac (claset()
	addIs [proper_abstr_subst, proper_VAR,
		abstr_proper_proper, subst_VAR_id]
	addss (simpset() addsimps [newFor_subst]))));
qed_spec_mp "abstr_subst";


(* Old version, when subst was defined via epsilon

Goalw [subst_def]
  "[| e (VAR n) = s; abstr e; newFor n (e dflt) |] ==> subst s n = e";
br some_equality 1;
by (Fast_tac 1);
by (REPEAT (etac conjE 1));
br abstr_extensionality 1;
by Auto_tac;
qed_spec_mp "abstr_subst";

*)


(* abstr_subst allows us to calculate the effect of substituting for a
   free variable (VAR n) directly via higher order unification, e.g.

Goal "subst (LAM x y z. x $$ VAR 0 $$ z) 0 = ?e";
br abstr_subst 1;
br refl 1;
by (abstr_tac [] [] 1);
by (asm_simp_tac (simpset() addsimps [newFor_LAM]
	addSolver (abstr_solver [dflt_def])) 1);

*)

(*

Goalw [proper_def, abstr_def]
  "proper s ==> ? e. abstr e & newFor n (e dflt) & e (VAR n) = s";
be levelSet.induct 1;
by (res_inst_tac [("x","% x. CON a")] exI 1);

by (case_tac "n=na" 2);
by (res_inst_tac [("x","% x. x")] exI 2);
by (res_inst_tac [("x","% x. VAR na")] exI 3);
by (res_inst_tac [("x","% x. BND j")] exI 4);
by (REPEAT (eresolve_tac [exE, conjE] 5));
by (res_inst_tac [("x","% x. e x $$ ea x")] exI 5);
by (REPEAT (eresolve_tac [exE, conjE] 6));
by (res_inst_tac [("x","% x. ABS (e x)")] exI 6);

by (ALLGOALS (fast_tac (claset()
	addIs abstSet.intrs
	addss simpset())));
qed "proper_subst_lemma";


Goalw [subst_def] "proper s ==> abstr (subst s n)";
br (proper_subst_lemma RS someI_ex RS conjunct1) 1;
ba 1;
qed "proper_abstr_subst";


Goalw [subst_def] "proper s ==> subst s n (VAR n) = s";
br (proper_subst_lemma RS someI_ex RS conjunct2 RS conjunct2) 1;
ba 1;
qed "proper_subst_id";


Goalw [subst_def] "proper s ==> newFor n (subst s n dflt)";
br (proper_subst_lemma RS someI_ex RS conjunct2 RS conjunct1) 1;
ba 1;
qed "proper_newFor_subst";

*)


Goal "abst i e ==> newFor n (lbind i e) = newFor n (e dflt)";
be abstSet.induct 1;
by (ALLGOALS (asm_simp_tac (simpset() addsimps lbind_simps)));
qed "abst_newFor_lbind";


Goalw [lambda_def, abstr_def]
  "abstr e ==> newFor n (LAM x. e x) = newFor n (e dflt)";
by (asm_simp_tac (simpset() addsimps [abst_newFor_lbind]) 1);
qed "newFor_LAM";


Goalw [abstr_def] "abstr e ==> ! t. proper t \
	\ --> newFor n t --> newFor n (e dflt) --> newFor n (e t)";
be abstSet.induct 1;
by (ALLGOALS (fast_tac (claset() addss simpset())));
qed_spec_mp "abstr_newFor_apply";


Goal "abst i e ==> gV (lbind i e) = gV (e dflt)";
be abstSet.induct 1;
by (ALLGOALS (asm_simp_tac (simpset() addsimps lbind_simps)));
qed "abst_gV_lbind";


Goalw [lambda_def, abstr_def]
  "abstr e ==> gV (LAM x. e x) = gV (e dflt)";
by (asm_simp_tac (simpset() addsimps [abst_gV_lbind]) 1);
qed "gV_LAM";


Goal "level i s ==> ! j. i = Suc j --> abst j (% x. inst j x s)";
be levelSet.induct 1;
by (ALLGOALS (fast_tac (claset()
	addIs abstSet.intrs
	addEs [less_SucE] addss simpset())));
val lemma = result();


Goal "level (Suc j) s ==> abst j (% x. inst j x s)";
bd lemma 1;
by Auto_tac;
qed "level_abst_inst";


Goalw [proper_def, abstr_def]
  "proper (ABS s) ==> abstr (% x. inst 0 x s)";
br level_abst_inst 1;
bes levelEs 1;
ba 1;
qed "proper_abstr_inst";


Goalw [proper_def, abstr_def]
  "abstr (% x. inst 0 x s) ==> proper (ABS s)";
bd abst_level_lbind 1;
by (full_simp_tac (simpset() addsimps [lbind_inst RS sym]) 1);
brs levelSet.intrs 1;
ba 1;
qed "abstr_inst_proper";


Goal "level i s ==> ! j. i = Suc j --> level 0 t --> level j (inst j t s)";
be levelSet.induct 1;
by (strip_tac 3);
bd level_0_level 3;
by (ALLGOALS (fast_tac (claset()
	addIs levelSet.intrs
	addEs [less_SucE]
	addss simpset())));
val lemma = result();


Goal "[| level (Suc j) s; level 0 t |] ==> level j (inst j t s)";
bd lemma 1;
by Auto_tac;
qed "level_inst";


Goal "level (Suc j) s ==> level j (inst j (VAR n) s)";
br level_inst 1;
ba 1;
brs levelSet.intrs 1;
qed "level_inst_VAR";


Goal "! i. size (inst i (VAR n) s) = size s";
by (induct_tac "s" 1);
by Auto_tac;
qed_spec_mp "size_inst_VAR";


val prems = Goalw [proper_def]
      "[| proper u; \
	\ !! a. P (CON a); \
	\ !! n. P (VAR n); \
	\ !! s t. [| proper s; P s; proper t; P t |] ==> P (s $$ t); \
	\ !! e. [| abstr e; ! n. P (e (VAR n)) |] ==> P (LAM x. e x) |] \
		\ ==> P u";
by (res_inst_tac [("P","level 0 u"), ("Q", "P u")] mp  1);
brs prems 2;
by (res_inst_tac [("P","% s. level 0 s --> P s"), ("f", "size")]
	measure_induct 1);
by (case_tac "x" 1);
by (ALLGOALS hyp_subst_tac);
by (strip_tac 5);
br (ABS_LAM RS ssubst) 5;
brs prems 5;
br allI 6;
by (eres_inst_tac [("x", "inst 0 (VAR n) expr")] allE 6);
by (ALLGOALS (fast_tac (claset()
	addIs prems @ [level_abst_inst, level_inst_VAR]
	addEs levelEs
	addss (simpset() addsimps [abstr_def, size_inst_VAR]))));
qed "proper_VAR_induct";


Goalw [proper_def, abstr_def, lambda_def]
  "abstr e ==> proper (LAM x. e x)";
brs levelSet.intrs 1;
br abst_level_lbind 1;
ba 1;
qed "abstr_proper_LAM";


val properIs = [proper_CON, proper_VAR, proper_APP, abstr_proper_LAM];


val prems = Goal "[| proper s; \
	\ !! a. s = CON a ==> P; \
	\ !! n. s = VAR n ==> P; \
	\ !! t u. [| s = t $$ u; proper t; proper u |] ==> P; \
	\ !! e. [| s = LAM x. e x; abstr e |] ==> P \
	\ |] ==> P";
by (cut_facts_tac prems 1);
by (subgoal_tac "s = s --> P" 1);
be impE 1;
br refl 1;
ba 1;
by (eres_inst_tac [("P","% x. (s=x --> P)")] proper_VAR_induct 1);
by (ALLGOALS (strip_tac THEN' eresolve_tac prems));
by (ALLGOALS atac);
qed "properE";