header {* + −
+ −
Nominal Isabelle Tutorial at POPL'11+ −
====================================+ −
+ −
Nominal Isabelle is a definitional extension of Isabelle/HOL, which+ −
means it does not add any new axioms to higher-order logic. It just+ −
adds new definitions and an infrastructure for 'nominal resoning'.+ −
+ −
+ −
The jEdit Interface+ −
-------------------+ −
+ −
The Isabelle theorem prover comes with an interface for jEdit. + −
Unlike many other theorem prover interfaces (e.g. ProofGeneral) where you + −
advance a 'checked' region in a proof script, this interface immediately + −
checks the whole buffer. The text you type is also immediately checked. + −
Malformed text or unfinished proofs are highlighted in red with a little + −
red 'stop' signal on the left-hand side. If you drag the 'red-box' cursor + −
over a line, the Output window gives further feedback. + −
+ −
Note: If a section is not parsed correctly, the work-around is to cut it + −
out and paste it back into the text (cut-out: Ctrl + X; paste in: Ctrl + V;+ −
Cmd is Ctrl on the Mac)+ −
+ −
Nominal Isabelle and jEdit can be started by typing on the command line+ −
+ −
isabelle jedit -l HOL-Nominal2+ −
isabelle jedit -l HOL-Nominal2 A.thy B.thy ...+ −
+ −
+ −
Symbols+ −
------- + −
+ −
Symbols can considerably improve the readability of your statements and proofs. + −
They can be input by just typing 'name-of-symbol' where 'name-of-symbol' is the + −
usual latex name of that symbol. A little window will then appear in which + −
you can select the symbol. With `Escape' you can ignore any suggestion.+ −
+ −
There are some handy short-cuts for frequently used symbols. + −
For example + −
+ −
short-cut symbol+ −
+ −
=> \<Rightarrow>+ −
==> \<Longrightarrow>+ −
--> \<longrightarrow>+ −
! \<forall> + −
? \<exists>+ −
/\ \<and>+ −
\/ \<or>+ −
~ \<not>+ −
~= \<noteq>+ −
: \<in>+ −
~: \<notin>+ −
+ −
For nominal the following two symbols have a special meaning+ −
+ −
\<sharp> sharp (freshness)+ −
\<bullet> bullet (permutation application)+ −
*}+ −
+ −
+ −
theory Tutorial1+ −
imports Lambda+ −
begin+ −
+ −
section {* Theories *}+ −
+ −
text {*+ −
All formal developments in Isabelle are part of a theory. A theory + −
needs to have a name and must import some pre-existing theory. The + −
imported theory will normally be Nominal2 (which provides many useful + −
concepts like set-theory, lists, nominal things etc). For the purpose + −
of this tutorial we import the theory Lambda.thy, which contains + −
already some useful definitions for (alpha-equated) lambda terms.+ −
*}+ −
+ −
+ −
+ −
section {* Types *}+ −
+ −
text {*+ −
Isabelle is based on simple types including some polymorphism. It also + −
includes overloading, which means that sometimes explicit type annotations + −
need to be given.+ −
+ −
- Base types include: nat, bool, string, lam (defined in the Lambda theory)+ −
+ −
- Type formers include: 'a list, ('a \<times> 'b), 'c set + −
+ −
- Type variables are written like in ML with an apostrophe: 'a, 'b, \<dots>+ −
+ −
Types known to Isabelle can be queried using the command "typ".+ −
*}+ −
+ −
typ nat+ −
typ bool+ −
typ string + −
typ lam -- {* alpha-equated lambda terms defined in Lambda.thy *}+ −
typ name -- {* type of (object) variables defined in Lambda.thy *}+ −
typ "('a \<times> 'b)" -- {* pair type *}+ −
typ "'c set" -- {* set type *}+ −
typ "'a list" -- {* list type *}+ −
typ "lam \<Rightarrow> nat" -- {* type of functions from lambda terms to natural numbers *}+ −
+ −
+ −
text {* Some malformed types - note the "stop" signal on the left margin *}+ −
+ −
(*+ −
typ boolean -- {* undeclared type *} + −
typ set -- {* type argument missing *}+ −
*)+ −
+ −
+ −
section {* Terms *}+ −
+ −
text {*+ −
Every term in Isabelle needs to be well-typed. However they can have + −
polymorphic type. Whether a term is accepted can be queried using+ −
the command "term". + −
*}+ −
+ −
term c -- {* a variable of polymorphic type *}+ −
term "1::nat" -- {* the constant 1 in natural numbers *}+ −
term 1 -- {* the constant 1 with polymorphic type *}+ −
term "{1, 2, 3::nat}" -- {* the set containing natural numbers 1, 2 and 3 *}+ −
term "[1, 2, 3]" -- {* the list containing the polymorphic numbers 1, 2 and 3 *}+ −
term "(True, ''c'')" -- {* a pair consisting of the boolean true and the string "c" *}+ −
term "Suc 0" -- {* successor of 0, in other words 1::nat *}+ −
term "Lam [x].Var x" -- {* a lambda-term *}+ −
term "App t1 t2" -- {* another lambda-term *}+ −
term "x::name" -- {* an (object) variable of type name *}+ −
term "atom (x::name)" -- {* atom is an overloaded function *}+ −
+ −
text {* + −
Lam [x].Var is the syntax we made up for lambda abstractions. If you+ −
prefer, you can have your own syntax (but \<lambda> is already taken up for + −
Isabelle's functions). We also came up with the type "name" for variables. + −
You can introduce your own types of object variables using the + −
command atom_decl: + −
*}+ −
+ −
atom_decl ident+ −
atom_decl ty_var+ −
+ −
text {*+ −
Isabelle provides some useful colour feedback about its constants (black), + −
free variables (blue) and bound variables (green).+ −
*}+ −
+ −
term "True" -- {* a constant defined somewhere...written in black *}+ −
term "true" -- {* not recognised as a constant, therefore it is interpreted+ −
as a free variable, written in blue *}+ −
term "\<forall>x. P x" -- {* x is bound (green), P is free (blue) *}+ −
+ −
+ −
text {* Formulae in Isabelle/HOL are terms of type bool *}+ −
+ −
term "True"+ −
term "True \<and> False"+ −
term "True \<or> B"+ −
term "{1,2,3} = {3,2,1}"+ −
term "\<forall>x. P x"+ −
term "A \<longrightarrow> B"+ −
term "atom a \<sharp> t" -- {* freshness in Nominal *}+ −
+ −
text {*+ −
When working with Isabelle, one deals with an object logic (that is HOL) and + −
Isabelle's rule framework (called Pure). Occasionally one has to pay attention + −
to this fact. But for the moment we ignore this completely.+ −
*}+ −
+ −
term "A \<longrightarrow> B" -- {* versus *}+ −
term "A \<Longrightarrow> B"+ −
+ −
term "\<forall>x. P x" -- {* versus *}+ −
term "\<And>x. P x"+ −
+ −
+ −
section {* Inductive Definitions: Evaluation Relation *}+ −
+ −
text {*+ −
In this section we want to define inductively the evaluation+ −
relation and for cbv-reduction relation.+ −
+ −
Inductive definitions in Isabelle start with the keyword "inductive" + −
and a predicate name. One can optionally provide a type for the predicate. + −
Clauses of the inductive predicate are introduced by "where" and more than + −
two clauses need to be separated by "|". One can also give a name to each + −
clause and indicate that it should be added to the hints database ("[intro]"). + −
A typical clause has some premises and a conclusion. This is written in + −
Isabelle as:+ −
+ −
"premise \<Longrightarrow> conclusion"+ −
"premise1 \<Longrightarrow> premise2 \<Longrightarrow> \<dots> premisen \<Longrightarrow> conclusion"+ −
+ −
There is an alternative way of writing the latter clause as+ −
+ −
"\<lbrakk>premise1; premise2; \<dots> premisen\<rbrakk> \<Longrightarrow> conclusion"+ −
+ −
If no premise is present, then one just writes+ −
+ −
"conclusion"+ −
+ −
Below we give two definitions for the transitive closure+ −
*}+ −
+ −
inductive+ −
eval :: "lam \<Rightarrow> lam \<Rightarrow> bool" (infixr "\<Down>" 60)+ −
where+ −
e_Lam[intro]: "Lam [x].t \<Down> Lam [x].t"+ −
| e_App[intro]: "\<lbrakk>t1 \<Down> Lam [x].t; t2 \<Down> v'; t[x::=v'] \<Down> v\<rbrakk> \<Longrightarrow> App t1 t2 \<Down> v"+ −
+ −
text {* + −
Values and cbv are also defined using inductive. + −
*}+ −
+ −
inductive+ −
val :: "lam \<Rightarrow> bool" + −
where+ −
v_Lam[intro]: "val (Lam [x].t)"+ −
+ −
section {* Theorems *}+ −
+ −
text {*+ −
A central concept in Isabelle is that of theorems. Isabelle's theorem+ −
database can be queried using + −
*}+ −
+ −
thm e_App+ −
thm e_Lam+ −
thm conjI+ −
thm conjunct1+ −
+ −
text {* + −
Notice that theorems usually contain schematic variables (e.g. ?P, ?Q, \<dots>). + −
These schematic variables can be substituted with any term (of the right type + −
of course). + −
+ −
When defining the predicates beta_star, Isabelle provides us automatically + −
with the following theorems that state how they can be introduced and what + −
constitutes an induction over them. + −
*}+ −
+ −
thm eval.intros+ −
thm eval.induct+ −
+ −
+ −
section {* Lemmas / Theorems / Corollaries *}+ −
+ −
text {*+ −
Whether to use lemma, theorem or corollary makes no semantic difference + −
in Isabelle. + −
+ −
A lemma starts with "lemma" and consists of a statement ("shows \<dots>") and + −
optionally a lemma name, some type-information for variables ("fixes \<dots>") + −
and some assumptions ("assumes \<dots>"). + −
+ −
Lemmas also need to have a proof, but ignore this 'detail' for the moment.+ −
+ −
Examples are+ −
*}+ −
+ −
lemma alpha_equ:+ −
shows "Lam [x].Var x = Lam [y].Var y"+ −
by (simp add: lam.eq_iff Abs1_eq_iff lam.fresh fresh_at_base)+ −
+ −
lemma Lam_freshness:+ −
assumes a: "atom y \<sharp> Lam [x].t"+ −
shows "(y = x) \<or> (y \<noteq> x \<and> atom y \<sharp> t)"+ −
using a by (auto simp add: lam.fresh Abs_fresh_iff) + −
+ −
lemma neutral_element:+ −
fixes x::"nat"+ −
shows "x + 0 = x"+ −
by simp+ −
+ −
text {*+ −
Note that in the last statement, the explicit type annotation is important + −
in order for Isabelle to be able to figure out what 0 stands for (e.g. a + −
natural number, a vector, etc) and which lemmas to apply.+ −
*}+ −
+ −
+ −
section {* Isar Proofs *}+ −
+ −
text {*+ −
Isar is a language for writing formal proofs that can be understood by humans + −
and by Isabelle. An Isar proof can be thought of as a sequence of 'stepping stones' + −
that start with some assumptions and lead to the goal to be established. Every such + −
stepping stone is introduced by "have" followed by the statement of the stepping + −
stone. An exception is the goal to be proved, which need to be introduced with "show".+ −
+ −
have "statement" \<dots>+ −
show "goal_to_be_proved" \<dots>+ −
+ −
Since proofs usually do not proceed in a linear fashion, labels can be given + −
to every stepping stone and they can be used later to explicitly refer to this + −
corresponding stepping stone ("using").+ −
+ −
have label: "statement1" \<dots>+ −
\<dots>+ −
have "later_statement" using label \<dots>+ −
+ −
Each stepping stone (or have-statement) needs to have a justification. The + −
simplest justification is "sorry" which admits any stepping stone, even false + −
ones (this is good during the development of proofs). + −
+ −
have "outrageous_false_statement" sorry+ −
+ −
Assumptions can be 'justified' using "by fact". + −
+ −
have "assumption" by fact+ −
+ −
Derived facts can be justified using + −
+ −
have "statement" by simp -- simplification + −
have "statement" by auto -- proof search and simplification + −
have "statement" by blast -- only proof search + −
+ −
If facts or lemmas are needed in order to justify a have-statement, then+ −
one can feed these facts into the proof by using "using label \<dots>" or + −
"using theorem-name \<dots>". More than one label at a time is allowed. For+ −
example+ −
+ −
have "statement" using label1 label2 theorem_name by auto+ −
+ −
Induction proofs in Isar are set up by indicating over which predicate(s) + −
the induction proceeds ("using a b") followed by the command "proof (induct)". + −
In this way, Isabelle uses defaults for which induction should be performed. + −
These defaults can be overridden by giving more information, like the variable + −
over which a structural induction should proceed, or a specific induction principle, + −
such as well-founded inductions. + −
+ −
After the induction is set up, the proof proceeds by cases. In Isar these + −
cases can be given in any order. Most commonly they are started with "case" and the + −
name of the case, and optionally some legible names for the variables used inside + −
the case. + −
+ −
In each "case", we need to establish a statement introduced by "show". Once + −
this has been done, the next case can be started using "next". When all cases + −
are completed, the proof can be finished using "qed".+ −
+ −
This means a typical induction proof has the following pattern+ −
+ −
proof (induct)+ −
case \<dots>+ −
\<dots>+ −
show \<dots>+ −
next+ −
case \<dots>+ −
\<dots>+ −
show \<dots>+ −
\<dots>+ −
qed+ −
+ −
Two very simple example proofs are as follows.+ −
*}+ −
+ −
subsection {* EXERCISE 1 *}+ −
+ −
lemma eval_val:+ −
assumes a: "val t"+ −
shows "t \<Down> t"+ −
using a+ −
proof (induct)+ −
case (v_Lam x t)+ −
show "Lam [x]. t \<Down> Lam [x]. t" sorry+ −
qed+ −
+ −
subsection {* EXERCISE 2 *}+ −
+ −
text {* Fill the gaps in the proof below. *}+ −
+ −
lemma eval_to_val:+ −
assumes a: "t \<Down> t'"+ −
shows "val t'"+ −
using a+ −
proof (induct)+ −
case (e_Lam x t)+ −
show "val (Lam [x].t)" sorry+ −
next+ −
case (e_App t1 x t t2 v v')+ −
-- {* all assumptions in this case *}+ −
have "t1 \<Down> Lam [x].t" by fact+ −
have ih1: "val (Lam [x]. t)" by fact+ −
have "t2 \<Down> v" by fact+ −
have ih2: "val v" by fact+ −
have "t [x ::= v] \<Down> v'" by fact+ −
have ih3: "val v'" by fact+ −
+ −
show "val v'" sorry+ −
qed+ −
+ −
+ −
section {* Datatypes: Evaluation Contexts*}+ −
+ −
text {*+ −
Datatypes can be defined in Isabelle as follows: we have to provide the name + −
of the datatype and a list its type-constructors. Each type-constructor needs + −
to have the information about the types of its arguments, and optionally + −
can also contain some information about pretty syntax. For example, we like+ −
to write "\<box>" for holes.+ −
*}+ −
+ −
datatype ctx = + −
Hole ("\<box>") + −
| CAppL "ctx" "lam"+ −
| CAppR "lam" "ctx"+ −
+ −
text {* Now Isabelle knows about: *}+ −
+ −
typ ctx+ −
term "\<box>"+ −
term "CAppL"+ −
term "CAppL \<box> (Var x)"+ −
+ −
subsection {* MINI EXERCISE *}+ −
+ −
text {*+ −
Try and see what happens if you apply a Hole to a Hole? + −
*}+ −
+ −
+ −
section {* Machines for Evaluation *}+ −
+ −
type_synonym ctxs = "ctx list"+ −
+ −
inductive+ −
machine :: "lam \<Rightarrow> ctxs \<Rightarrow>lam \<Rightarrow> ctxs \<Rightarrow> bool" ("<_,_> \<mapsto> <_,_>" [60, 60, 60, 60] 60)+ −
where+ −
m1: "<App t1 t2, Es> \<mapsto> <t1, (CAppL \<box> t2) # Es>"+ −
| m2: "val v \<Longrightarrow> <v, (CAppL \<box> t2) # Es> \<mapsto> <t2, (CAppR v \<box>) # Es>"+ −
| m3: "val v \<Longrightarrow> <v, (CAppR (Lam [x].t) \<box>) # Es> \<mapsto> <t[x ::= v], Es>"+ −
+ −
text {*+ −
Since the machine defined above only performs a single reduction,+ −
we need to define the transitive closure of this machine. *}+ −
+ −
inductive + −
machines :: "lam \<Rightarrow> ctxs \<Rightarrow> lam \<Rightarrow> ctxs \<Rightarrow> bool" ("<_,_> \<mapsto>* <_,_>" [60, 60, 60, 60] 60)+ −
where+ −
ms1: "<t,Es> \<mapsto>* <t,Es>"+ −
| ms2: "\<lbrakk><t1,Es1> \<mapsto> <t2,Es2>; <t2,Es2> \<mapsto>* <t3,Es3>\<rbrakk> \<Longrightarrow> <t1,Es1> \<mapsto>* <t3,Es3>"+ −
+ −
declare machine.intros[intro] machines.intros[intro]+ −
+ −
section {* EXERCISE 3 *}+ −
+ −
text {*+ −
We need to show that the machines-relation is transitive.+ −
Fill in the gaps below. + −
*}+ −
+ −
lemma ms3[intro]:+ −
assumes a: "<e1, Es1> \<mapsto>* <e2, Es2>" + −
and b: "<e2, Es2> \<mapsto>* <e3, Es3>"+ −
shows "<e1, Es1> \<mapsto>* <e3, Es3>"+ −
using a b+ −
proof(induct)+ −
case (ms1 e1 Es1)+ −
have c: "<e1, Es1> \<mapsto>* <e3, Es3>" by fact+ −
+ −
show "<e1, Es1> \<mapsto>* <e3, Es3>" sorry+ −
next+ −
case (ms2 e1 Es1 e2 Es2 e2' Es2') + −
have ih: "<e2', Es2'> \<mapsto>* <e3, Es3> \<Longrightarrow> <e2, Es2> \<mapsto>* <e3, Es3>" by fact+ −
have d1: "<e2', Es2'> \<mapsto>* <e3, Es3>" by fact+ −
have d2: "<e1, Es1> \<mapsto> <e2, Es2>" by fact+ −
+ −
show "<e1, Es1> \<mapsto>* <e3, Es3>" sorry+ −
qed+ −
+ −
text {* + −
Just like gotos in the Basic programming language, labels often reduce + −
the readability of proofs. Therefore one can use in Isar the notation+ −
"then have" in order to feed a have-statement to the proof of + −
the next have-statement. This is used in the second case below.+ −
*}+ −
+ −
lemma + −
assumes a: "<e1, Es1> \<mapsto>* <e2, Es2>" + −
and b: "<e2, Es2> \<mapsto>* <e3, Es3>"+ −
shows "<e1, Es1> \<mapsto>* <e3, Es3>"+ −
using a b+ −
proof(induct)+ −
case (ms1 e1 Es1)+ −
show "<e1, Es1> \<mapsto>* <e3, Es3>" by fact+ −
next+ −
case (ms2 e1 Es1 e2 Es2 e2' Es2')+ −
have ih: "<e2', Es2'> \<mapsto>* <e3, Es3> \<Longrightarrow> <e2, Es2> \<mapsto>* <e3, Es3>" by fact+ −
have "<e2', Es2'> \<mapsto>* <e3, Es3>" by fact+ −
then have d3: "<e2, Es2> \<mapsto>* <e3, Es3>" using ih by simp+ −
have d2: "<e1, Es1> \<mapsto> <e2, Es2>" by fact+ −
show "<e1, Es1> \<mapsto>* <e3, Es3>" using d2 d3 by auto+ −
qed+ −
+ −
text {* + −
The label ih2 cannot be got rid of in this way, because it is used + −
two lines below and we cannot rearrange them. We can still avoid the+ −
label by feeding a sequence of facts into a proof using the + −
"moreover"-chaining mechanism:+ −
+ −
have "statement_1" \<dots>+ −
moreover+ −
have "statement_2" \<dots>+ −
\<dots>+ −
moreover+ −
have "statement_n" \<dots>+ −
ultimately have "statement" \<dots>+ −
+ −
In this chain, all "statement_i" can be used in the proof of the final + −
"statement". With this we can simplify our proof further to:+ −
*}+ −
+ −
lemma + −
assumes a: "<e1, Es1> \<mapsto>* <e2, Es2>" + −
and b: "<e2, Es2> \<mapsto>* <e3, Es3>"+ −
shows "<e1, Es1> \<mapsto>* <e3, Es3>"+ −
using a b+ −
proof(induct)+ −
case (ms1 e1 Es1)+ −
show "<e1, Es1> \<mapsto>* <e3, Es3>" by fact+ −
next+ −
case (ms2 e1 Es1 e2 Es2 e2' Es2')+ −
have ih: "<e2', Es2'> \<mapsto>* <e3, Es3> \<Longrightarrow> <e2, Es2> \<mapsto>* <e3, Es3>" by fact+ −
have "<e2', Es2'> \<mapsto>* <e3, Es3>" by fact+ −
then have "<e2, Es2> \<mapsto>* <e3, Es3>" using ih by simp+ −
moreover+ −
have "<e1, Es1> \<mapsto> <e2, Es2>" by fact+ −
ultimately show "<e1, Es1> \<mapsto>* <e3, Es3>" by auto+ −
qed+ −
+ −
+ −
text {* + −
While automatic proof procedures in Isabelle are not able to prove statements+ −
like "P = NP" assuming usual definitions for P and NP, they can automatically+ −
discharge the lemmas we just proved. For this we only have to set up the induction+ −
and auto will take care of the rest. This means we can write:+ −
*}+ −
+ −
lemma + −
assumes a: "val t"+ −
shows "t \<Down> t"+ −
using a by (induct) (auto)+ −
+ −
lemma + −
assumes a: "t \<Down> t'"+ −
shows "val t'"+ −
using a by (induct) (auto)+ −
+ −
lemma+ −
assumes a: "<e1, Es1> \<mapsto>* <e2, Es2>" + −
and b: "<e2, Es2> \<mapsto>* <e3, Es3>"+ −
shows "<e1, Es1> \<mapsto>* <e3, Es3>"+ −
using a b by (induct) (auto)+ −
+ −
+ −
section {* EXERCISE 4 *}+ −
+ −
text {*+ −
The point of the machine is that it simulates the evaluation+ −
relation. Therefore we like to prove the following:+ −
*}+ −
+ −
theorem + −
assumes a: "t \<Down> t'"+ −
shows "<t, []> \<mapsto>* <t', []>"+ −
using a + −
proof (induct)+ −
case (e_Lam x t) + −
-- {* no assumptions *}+ −
show "<Lam [x].t, []> \<mapsto>* <Lam [x].t, []>" by auto+ −
next+ −
case (e_App t1 x t t2 v' v) + −
-- {* all assumptions in this case *}+ −
have a1: "t1 \<Down> Lam [x].t" by fact+ −
have ih1: "<t1, []> \<mapsto>* <Lam [x].t, []>" by fact+ −
have a2: "t2 \<Down> v'" by fact+ −
have ih2: "<t2, []> \<mapsto>* <v', []>" by fact+ −
have a3: "t[x::=v'] \<Down> v" by fact+ −
have ih3: "<t[x::=v'], []> \<mapsto>* <v, []>" by fact+ −
+ −
-- {* your reasoning *}+ −
show "<App t1 t2, []> \<mapsto>* <v, []>" sorry+ −
qed+ −
+ −
+ −
text {* + −
Again the automatic tools in Isabelle can discharge automatically + −
of the routine work in these proofs. We can write: + −
*}+ −
+ −
theorem eval_implies_machines_ctx:+ −
assumes a: "t \<Down> t'"+ −
shows "<t, Es> \<mapsto>* <t', Es>"+ −
using a+ −
by (induct arbitrary: Es)+ −
(metis eval_to_val machine.intros ms1 ms2 ms3 v_Lam)++ −
+ −
corollary eval_implies_machines:+ −
assumes a: "t \<Down> t'"+ −
shows "<t, []> \<mapsto>* <t', []>"+ −
using a eval_implies_machines_ctx by simp+ −
+ −
+ −
section {* Function Definitions: Filling a Lambda-Term into a Context *}+ −
+ −
text {*+ −
Many functions over datatypes can be defined by recursion on the+ −
structure. For this purpose, Isabelle provides "fun". To use it one needs + −
to give a name for the function, its type, optionally some pretty-syntax + −
and then some equations defining the function. Like in "inductive",+ −
"fun" expects that more than one such equation is separated by "|".+ −
*}+ −
+ −
fun+ −
filling :: "ctx \<Rightarrow> lam \<Rightarrow> lam" ("_\<lbrakk>_\<rbrakk>" [100, 100] 100)+ −
where+ −
"\<box>\<lbrakk>t\<rbrakk> = t"+ −
| "(CAppL E t')\<lbrakk>t\<rbrakk> = App (E\<lbrakk>t\<rbrakk>) t'"+ −
| "(CAppR t' E)\<lbrakk>t\<rbrakk> = App t' (E\<lbrakk>t\<rbrakk>)"+ −
+ −
+ −
text {* + −
After this definition Isabelle will be able to simplify+ −
statements like: + −
*}+ −
+ −
lemma + −
shows "(CAppL \<box> (Var x))\<lbrakk>Var y\<rbrakk> = App (Var y) (Var x)"+ −
by simp+ −
+ −
fun + −
ctx_compose :: "ctx \<Rightarrow> ctx \<Rightarrow> ctx" (infixr "\<odot>" 99)+ −
where+ −
"\<box> \<odot> E' = E'"+ −
| "(CAppL E t') \<odot> E' = CAppL (E \<odot> E') t'"+ −
| "(CAppR t' E) \<odot> E' = CAppR t' (E \<odot> E')"+ −
+ −
fun+ −
ctx_composes :: "ctxs \<Rightarrow> ctx" ("_\<down>" [110] 110)+ −
where+ −
"[]\<down> = \<box>"+ −
| "(E # Es)\<down> = (Es\<down>) \<odot> E"+ −
+ −
text {* + −
Notice that we not just have given a pretty syntax for the functions, but+ −
also some precedences. The numbers inside the [\<dots>] stand for the precedences+ −
of the arguments; the one next to it the precedence of the whole term.+ −
+ −
This means we have to write (Es1 \<odot> Es2) \<odot> Es3 otherwise Es1 \<odot> Es2 \<odot> Es3 is+ −
interpreted as Es1 \<odot> (Es2 \<odot> Es3).+ −
*}+ −
+ −
section {* Structural Inductions over Contexts *}+ −
+ −
text {*+ −
So far we have had a look at an induction over an inductive predicate. + −
Another important induction principle is structural inductions for + −
datatypes. To illustrate structural inductions we prove some facts+ −
about context composition, some of which we will need later on.+ −
*}+ −
+ −
subsection {* EXERCISE 5 *}+ −
+ −
text {* Complete the proof and remove the sorries. *}+ −
+ −
lemma ctx_compose:+ −
shows "(E1 \<odot> E2)\<lbrakk>t\<rbrakk> = E1\<lbrakk>E2\<lbrakk>t\<rbrakk>\<rbrakk>"+ −
proof (induct E1)+ −
case Hole+ −
show "(\<box> \<odot> E2)\<lbrakk>t\<rbrakk> = \<box>\<lbrakk>E2\<lbrakk>t\<rbrakk>\<rbrakk>" sorry+ −
next+ −
case (CAppL E1 t')+ −
have ih: "(E1 \<odot> E2)\<lbrakk>t\<rbrakk> = E1\<lbrakk>E2\<lbrakk>t\<rbrakk>\<rbrakk>" by fact+ −
show "((CAppL E1 t') \<odot> E2)\<lbrakk>t\<rbrakk> = (CAppL E1 t')\<lbrakk>E2\<lbrakk>t\<rbrakk>\<rbrakk>" sorry+ −
next+ −
case (CAppR t' E1)+ −
have ih: "(E1 \<odot> E2)\<lbrakk>t\<rbrakk> = E1\<lbrakk>E2\<lbrakk>t\<rbrakk>\<rbrakk>" by fact+ −
show "((CAppR t' E1) \<odot> E2)\<lbrakk>t\<rbrakk> = (CAppR t' E1)\<lbrakk>E2\<lbrakk>t\<rbrakk>\<rbrakk>" sorry+ −
qed+ −
+ −
+ −
subsection {* EXERCISE 6 *}+ −
+ −
text {*+ −
Remove the sorries in the ctx_append proof below. You can make + −
use of the following two properties. + −
*}+ −
+ −
lemma neut_hole:+ −
shows "E \<odot> \<box> = E"+ −
by (induct E) (simp_all)+ −
+ −
lemma compose_assoc: + −
shows "(E1 \<odot> E2) \<odot> E3 = E1 \<odot> (E2 \<odot> E3)"+ −
by (induct E1) (simp_all)+ −
+ −
lemma+ −
shows "(Es1 @ Es2)\<down> = (Es2\<down>) \<odot> (Es1\<down>)"+ −
proof (induct Es1)+ −
case Nil+ −
show "([] @ Es2)\<down> = Es2\<down> \<odot> []\<down>" sorry+ −
next+ −
case (Cons E Es1)+ −
have ih: "(Es1 @ Es2)\<down> = Es2\<down> \<odot> Es1\<down>" by fact+ −
+ −
show "((E # Es1) @ Es2)\<down> = Es2\<down> \<odot> (E # Es1)\<down>" sorry+ −
qed+ −
+ −
text {* + −
The last proof involves several steps of equational reasoning.+ −
Isar provides some convenient means to express such equational + −
reasoning in a much cleaner fashion using the "also have" + −
construction. + −
*}+ −
+ −
lemma + −
shows "(Es1 @ Es2)\<down> = (Es2\<down>) \<odot> (Es1\<down>)"+ −
proof (induct Es1)+ −
case Nil+ −
show "([] @ Es2)\<down> = Es2\<down> \<odot> []\<down>" using neut_hole by simp+ −
next+ −
case (Cons E Es1)+ −
have ih: "(Es1 @ Es2)\<down> = Es2\<down> \<odot> Es1\<down>" by fact+ −
have "((E # Es1) @ Es2)\<down> = (E # (Es1 @ Es2))\<down>" by simp+ −
also have "\<dots> = (Es1 @ Es2)\<down> \<odot> E" by simp+ −
also have "\<dots> = (Es2\<down> \<odot> Es1\<down>) \<odot> E" using ih by simp+ −
also have "\<dots> = Es2\<down> \<odot> (Es1\<down> \<odot> E)" using compose_assoc by simp+ −
also have "\<dots> = Es2\<down> \<odot> (E # Es1)\<down>" by simp+ −
finally show "((E # Es1) @ Es2)\<down> = Es2\<down> \<odot> (E # Es1)\<down>" by simp+ −
qed+ −
+ −
+ −
end + −
+ −