request clarification on appearance of apparent real coercion in theory involving natural numbers in Isabelle theory - isabelle

I am examining the following theory in Isabelle2020 /jEdit:
theory Sqrt
imports Complex_Main "HOL-Computational_Algebra.Primes"
begin
theorem
assumes "prime (p::nat)"
shows "sqrt p ∉ ℚ"
proof
from ‹prime p› have p: "1 < p" by (simp add: prime_nat_iff)
assume "sqrt p ∈ ℚ"
then obtain m n :: nat where
n: "n ≠ 0" and sqrt_rat: "¦sqrt p¦ = m / n"
and "coprime m n" by (rule Rats_abs_nat_div_natE)
[we omit the remainder of the proof]
The Output pane shows proof state:
have (⋀m n. n ≠ 0 ⟹ ¦sqrt (real p)¦ = real m / real n ⟹ coprime m n ⟹ ?thesis) ⟹ ?thesis
proof (state)
this:
n ≠ 0
¦sqrt (real p)¦ = real m / real n
coprime m n
goal (1 subgoal):
1. sqrt (real p) ∈ ℚ ⟹ False
My question is: Are those appearances of "real" a type coercion? I have read Chapter 8 discussing types in the so-called tutorial that accompanies the Isabelle distribution (title A Proof Assistant for Higher-Order Logic). I read Florian Haftman's document title Isabelle/HOL type-class hierarchy (also part of the Isabelle distribution). The rule used in the theory statements above, Rats_abs_nat_div_natE, is a lemma in the Real.thy theory.
I chased down the reference in that theory file and looked at §8.4.5 in A Proof
Assistant for Higher-Order Logic where I found that The natural
number type nat is a linearly ordered semiring, type int is an ordered ring,
and type real is an ordered field. Properties may not hold for a particular class, e.g., no abstract properties involving subtraction hold for type nat (since, of course, one might end up with a negative number, which would not be a natural number). Instead specific theorems are provided addressing subtraction on the type nat. More to the point, “all abstract properties involving division require a field." (A Proof Assistant for Higher-Order Logic.)
So, are we are seeing here a quotient type being used to lift a division of natural or integer types to the abstract real type in order to satisfy the field
requirement (see §11.9 The Isabelle/Isar Reference Manual)? The quotient type real is created from the equivalence relation definition realrel in the Real.thy file.
I was surprised to see real terms in a proof depending on primes, positive integers, and rational numbers and wanted to assure that I had at least gotten close to the explanation why this is occuring in the Isabelle proof.

The function sqrt is only defined over reals. Therefore, you need to convert its argument p from nat to real. There is a coercion that does that automatically for you; hence the real function you can.
After that, the only way to type m/n is real m / real n.
Generally, overloaded syntax is a problematic for proof assistants. For example, 2/3 on paper can be the rational number Fract 2 3 in Isabelle, the real number 2/3, or the inverse of 3 in a F_5 multiplied by two, or something else.
In Isabelle this is solved by (to a certain extend) avoiding overloading and using different notations.

Related

What is `ind` type in Isabelle

In Isabelle natural numbers are defined as follows
typedecl ind
axiomatization Zero_Rep :: ind and Suc_Rep :: "ind ⇒ ind"
― ‹The axiom of infinity in 2 parts:›
where Suc_Rep_inject: "Suc_Rep x = Suc_Rep y ⟹ x = y"
and Suc_Rep_not_Zero_Rep: "Suc_Rep x ≠ Zero_Rep"
subsection ‹Type nat›
text ‹Type definition›
inductive Nat :: "ind ⇒ bool"
where
Zero_RepI: "Nat Zero_Rep"
| Suc_RepI: "Nat i ⟹ Nat (Suc_Rep i)"
That's a lot of code to write what's effectively just
datatype nat = Zero | Suc nat
Is there some greater purpose to ind or maybe it is there just for historical reasons?
The datatype package needs a whole lot of maths to do all those internal constructions that are required to give you the datatype you want in the end. In particular, it needs natural numbers.
So the reason why the datatype package is not used to define the naturals is that it simply isn't available yet at that point.
One could of course just axiomatise the nat type directly. I think the idea to instead axiomatise some infinite type and then carve the naturals out of that is a standard one, I think it is done similarly in Zermelo–Fraenkel logic.
Side note: In fact one could even make the datatype package itself axiomatic. But the common philosophy in interactive theorem provers, especially in the LCF family, is to work from a small set of axioms and that everything that can be constructed should be constructed instead of axiomatised. This reduces the amount of "trusted code".
A direct axiomatisation of the natural numbers would not be very controversial I think, but for something as complicated as the datatype package an axiomatic implementation would h introduce lots of possibilities for subtle soundness bugs.

Quantifying over a specific part of the sentence in Isabelle

Suppose I write a lemma "(∀a. P a ⟹ Q a) ⟹ R b" in Isabelle. ∀a will only quantify over P a. If I want to quantify over P a ⟹ Q a however, putting parenthesis after ∀a (i.e "(∀a. (P a ⟹ Q a)) ⟹ R a") will cause Isabelle's parsing to fail.
How can I properly quantify over a specific part of the sentence?
Note: I know that free variables in lemmas are implicitly universal in Isabelle. This question is mostly for inner statements, in which the quantifier should not range over the whole sentence.
I believe that the parse failure stems from the fact that Isabelle/HOL has two distinct types of implication operators, Pure.imp (⟹) and HOL.implies (⟶). The former is part of the Isabelle metalogic while the latter is part of the HOL logic. You can find more information about why this distinction exists and when to use each in these mailing list posts:
https://lists.cam.ac.uk/pipermail/cl-isabelle-users/2018-December/msg00031.html
https://lists.cam.ac.uk/pipermail/cl-isabelle-users/2019-January/msg00019.html
The ⟹ operator has very low precedence, lower than ∀ and as a result ∀a. (P a ⟹ Q a) cannot be parsed as you would expect. You may fix the parse failure in your lemma by using the ⟶ operator instead which has higher precedence than ∀. Another option is to change ∀ to the meta quantifier ⋀, making your sentence more in line with Isabelle's framework.
A table of operator precedence is available but I cannot guarantee that it is up to date. You can use the print_syntax command in Isabelle for a more reliably up-to-date ordering.
Table: https://lists.cam.ac.uk/pipermail/cl-isabelle-users/2012-November/pdfi7tZP06fqA.pdf

Isabelle - exI and refl behavior explanation needed

I am trying to understand the lemma below.
Why is the ?y2 schematic variable introduced in exI?
And why it is not considered in refl (so: x = x)?
lemma "∀x. ∃y. x = y"
apply(rule allI) (* ⋀x. ∃y. x = y *)
thm exI (* ?P ?x ⟹ ∃x. ?P x *)
apply(rule exI) (* ⋀x. x = ?y2 x *)
thm refl (* ?t = ?t *)
apply(rule refl)
done
UPDATE (because I can't format code in comments):
This is the same lemma with a different proof, using simp.
lemma "∀x. ∃y. x = y"
using [[simp_trace, simp_trace_depth_limit = 20]]
apply (rule allI) (*So that we start from the same problem state. *)
apply (simp only:exI)
done
The trace shows:
[0]Adding rewrite rule "HOL.exI":
?P1 ?x1 ⟹ ∃x. ?P1 x ≡ True
[1]SIMPLIFIER INVOKED ON THE FOLLOWING TERM:
⋀x. ∃y. x = y
[1]Applying instance of rewrite rule "HOL.exI":
?P1 ?x1 ⟹ ∃x. ?P1 x ≡ True
[1]Trying to rewrite:
x = ?x1 ⟹ ∃xa. x = xa ≡ True <-- NOTE: not ?y2 xa or similar!
[2]SIMPLIFIER INVOKED ON THE FOLLOWING TERM:
x = ?x1
[1]SUCCEEDED
∃xa. x = xa ≡ True
So apparently simp and rule handles exI differently. And the remaining question is: what is the mechanical (programmatical) reasoning behind rule's behavior.
When you use rule thm for some fact thm, Isabelle performs higher-order unification of the conclusion of thm with the current goal. If there is a unifier, it is used to instantiate both the goal and the conclusion of the theorem, and then resolution is performed (i.e. the goal is replaced with the assumptions of thm).
This means that:
Schematic variables in the goal can be instantiated by rule through unification
Variables that appear only in the assumptions of thm will not be instantiated by the unification and will therefore remain schematic. That way, you end up with schematic variables in your new goals. Such variables can be seen as existential in some sense, because the conclusion of thm holds if you can prove the assumptions for just one arbitrary value.
In the case of exI, you have ?P ?x ⟹ ∃x. ?P x. When you apply rule exI, the variable ?P is instantiated to λy. x = y, but the variable ?x appears only in the assumptions of exI, so it remains schematic. This means that you can pick any value you want for ?x later on in your proof.
To be more precise, you end up with ⋀x. x = ?y2 x as your goal. You might ask ‘Why not just ⋀x. x = ?y2?’ That would mean that you have to show that x equals some fixed value y2 for all possible values of x. That is obviously not true in general. ⋀x. x = ?y2 x means you have to show that every x equals some y2 that may depend on x – or, equivalently, that there is a function y2 that, when given x, outputs x.
Of course, there is such a function and it is simply the identity function λx. x. That is precisely what ?y2 gets instantiated to when you apply rule refl: the goal x = ?y2 x is unified with the conclusion of refl ?t = ?t and you end up with ?t = x and ?y2 = λx. x, and since refl has no assumptions, this resolution finishes the proof.
I am not entirely sure what you mean with ‘And why it is not considered in refl?’, but I hope that I have answered your questions.
Get a more complete answer from an expert, but I give a short, brief answer to your second part.
The great thing about Isabelle is that it provides many different ways to prove a problem.
Your new question is similar to L.Paulson's comment on FOM: you moved the goal post by switching the question to rule vs. simp:
http://www.cs.nyu.edu/pipermail/fom/2015-October/019312.html
Getting a basic understanding of simp is actually a much easier goal to pursue, or I wouldn't be adding my reponse here.
rule and natural deduction
The use of rule is the use of natural deduction (ND), where most people aren't up to speed on ND. The use of ND requires understanding ND, so questions like your first question can lead to a non-simple answer, because anything informative can't be a one-liner answer, especially due to things like schematic variables (which you asked about), resolution, unification, rewriting, etc.
Do a search on natural deduction and you'll find the standard wiki page about it. There are numerous books on natural deduction, though they get swamped in searches on "logic" due to first-order logic books. A popular book is Logic in Computer Science, 2nd, by Huth and Ryan.
If you study ND, you'll see that exI matches one of the ND rules.
I have yet to take the time to come up to speed on ND, because I keep making progress without having more than a basic understanding of ND.
Sledgehammer, and auto-methods auto, simp, blast, induct, cases, etc., and Sledgehammer's use of some of those, keep me from finding the time to become good with natural decution.
Answer's like M.Eberl's, though not simple explanations, help me absorb a little here and a little there.
Simp, I think of it as simple substitution (rewriting)
The mechanics behind simp is really simple, compared to natural deduction. You define a formula and prove it:
lemma foo [simp]: "left_hand_side = right_hand_side"
In the proof of another theorem, when simp is invoked in one way or another, or foo is unfolded, where there is left_hand_side, it's replaced with right_hand_side. It's just classic mathematical substitution.
I suppose it could also be "rewriting", but I don't know anything about rewriting, other than they talk about it.
There are lots of details about how and whether one should set things up automatically (to prevent looping), like with [simp] or declare foo_def [simp add], but that's just details along the line of normal programming.

What is a Quotient type pattern in Isabelle?

What is a "Quotient type pattern" in Isabelle?
I couldn't find any explanation over the internet.
It would be better if you would quote a little from where you saw the phrase. I know of "pattern matching," and I know of "quotient type," but I don't know of "quotient type pattern."
I prefer not to ask for clarification, and then wait, so I pick two of the three words, "quotient type." If I'm on the wrong track, it's still a worthy subject, and a big and important part of Isabelle/HOL.
There is the quotient_type keyword, and it allows you to define a new type with an equivalence relation.
It is part of the quotient package, described starting on page 248 of isar-ref.pdf. There happens to be a Wiki page, Quotient_type.
A more involved description is given by Brian Hufmann and Ondřej Kunčar. Go to Kunčar's web page and look at the two PDFs titled Lifting and Transfer: A Modular Design for Quotients in Isabelle/HOL, which are not exactly the same.
It happens to be that lifting and quotient types are heavily related, and not easy to understand, which is why I try to study a little here and there, like right now, to get a better understanding of it all.
Integers and Rationals in HOL Are Quotient Types, I Pick One as an Example, Integers
You can start by looking Int.thy.
For a quotient type, you need an equivalence relation, which defines a set, and intrel is what is used to define that set for type int.
definition intrel :: "(nat * nat) => (nat * nat) => bool" where
"intrel = (%(x, y) (u, v). x + v = u + y)"
This is the classic definition of the integers, based on the natural numbers. Integers are ordered pairs of natural numbers (and sets as I describe below), and they're equal by that definition.
For example, informally, (2,3) = (4,5) because 2 + 5 = 4 + 3.
I'm boring you, and you're waiting for the good stuff. Here's part of it, the use of quotient_type:
quotient_type int = "nat * nat" / "intrel"
morphisms Rep_Integ Abs_Integ
Those two morphisms come into play, if you want to strain your brain, and really understand what's going on, which I do. There are lots of functions and simp rules that quotient_type generates, and you have to do a lot of work to find it all, such as with the find_theorems command.
An Abs function abstracts an ordered pair to an int. Check these out:
lemma "Abs_Integ(1,0) = (1::int)"
by(metis one_int_def)
lemma "Abs_Integ(x,0) + Abs_Integ(y,0) ≥ (0::int)"
by(smt int_def)
They show that an int really is an ordered pair, under the hood of the engine.
Now I show the explicit types of those morphisms, along with Abs_int and Rep_int, which show int not only as an ordered pair, but as a set of ordered pairs.
term "Abs_int :: (nat * nat) set => int"
term "Abs_Integ :: (nat * nat) => int"
term "Rep_int :: int => (nat * nat) set"
term "Rep_Integ :: int => (nat * nat)"
I'm boring you again, but I have an emotional need to show some more examples. Two positive integers are equal if the components of the ordered pairs differ by one, such as these:
lemma "Abs_Integ(1,0) = Abs_Integ(3,2)"
by(smt nat.abs_eq split_conv)
lemma "Abs_Integ(4,3) = Abs_Integ(3,2)"
by(smt nat.abs_eq split_conv)
What would you expect if you added Abs_Integ(4,3) and Abs_Integ(3,2)? This:
lemma "Abs_Integ(2,3) + Abs_Integ(3,4) = Abs_Integ(2 + 3, 3 + 4)"
by(metis plus_int.abs_eq plus_int_def split_conv)
That plus_int in the proof is defined in Int.thy, on line 44.
lift_definition plus_int :: "int => int => int"
is "%(x, y) (u, v). (x + u, y + v)"
What is this lifting all about? That would put me at "days into" this explanation, and I'm only just starting to understand it a little.
The find_theorems shows there's lots of stuff hidden, as I said:
thm "plus_int.abs_eq"
find_theorems name: "Int.plus_int*"
More examples, but these are to emphasize that, under the hood of the engine, an int ties back into an equivalence class as a set, where I'm using intrel above to define the sets right:
term "Abs_int::(nat * nat) set => int"
term "Abs_int {(x,y). x + 3 = 2 + y}" (*(2,3)*)
term "Abs_int {(x,y). x + 4 = 3 + y}" (*(3,4)*)
lemma "Abs_int {(x,y). x + 3 = 2 + y} = Abs_int {(x,y). x + 100 = 99 + y}"
by(auto)
That auto proof was easy, but there's no magic coming through for me on this next one, even though it's simple.
lemma "Abs_int {(x,y). x + 3 = 2 + y} + Abs_int {(x,y). x + 4 = 3 + y}
= Abs_int {(x,y). x + 7 = 5 + y}"
apply(auto simp add: plus_int.abs_eq plus_int_def intrel_def)
oops
It could be that all I need to do is tap into something that's not a simp rule by default.
If quotient_type is not the "quotient type pattern" you're talking about, at least I got something out of it by seeing all what find_theorems returns about Int.plus_int* above.
What is a quotient type?
A quotient type is a way to define a new type in terms of an already existing type. That way, we don't have to axiomatize the new type. For example, one might find reasonable to use the naturals to build the integers, since they can be seen as "naturals+negatives". You may then want to use the integers to build the rationals, since they can be seen as "integers+quotients". And so on.
Quotient types use a given equivalence relation on the "lower type" to determine what equality means for the "higher type".
Being more precise: A quotient type is an abstract type for which equality is dictated by some equivalence relation on its underlying representation.
This definition might be too abstract at first, so we'll use the integers as a grounding example.
Example: Integers from Naturals
If one wants to define the integers, the most standard way is to use an ordered pair of natural numbers, such as (a,b), which intuitively represents "a-b". For example, the number represented by the pair (2,4) is -2, since intuitively 2-4 = -2. By the same logic, (0,2) also represents '-2', and so does (1,3) or (10,12), since 0-2 = 1-3 = 10-12 = -2.
We could then say that "two pairs (a,b) and (x,y) represent the same integer iff a - b = x - y". However, the minus operation can be weird in natural numbers (what is '2-3' in the naturals?). To avoid that weirdness, rewrite 'a - b = x - y' as 'a + y = x + b', now using only addition. So, two pairs (a,b) and (x,y) represent the same integer when 'a + y = x + b'. For example, (7,9) represents the same integer as (1,3), since '7 + 3 = 1 + 9'.
That leads to a quotient definition of integers: An integer is a type represented by an ordered pair of natural numbers. Two integers represented by (a,b) and (x,y) are equal if, and only if, a+y = x+b.
The integer type derives from the type "ordeded pair of natural numbers" which is its representation. We may call the integer itself an abstraction of that. The equality of integers is defined as whenever some underlying representations '(a,b)' and '(x,y)' follow the equivalence relation 'a+y = x+b'.
In that sense, the integer '-3' is represented by both '(0,3)' and '(2,5)', and we may show this by noticing that 0+5 = 3+2. On the other hand, '(0,3)' and '(6,10)' do not represent the same integer, since '0+10 ≠ 3+6'. This reflects the fact that '-3 ≠ -4'.
Technically speaking, the integer '-3' is not specifically '(0,3)', nor '(1,4)', nor '(10,13)', but the whole equivalence class. By that I mean that '-3' is the set containing all of its representations (i.e. -3 = { (0,3), (1,4), (2,5), (3,6), (4,7), ... }). '(0,3)' is called a representation for '-3', and '-3' is the abstraction of '(0,3)'.
Morphisms: Rep and Abs in Isabelle
Rep and Abs are ways for us to transition between the representations and the abstractions they represent. More precisely, they are mappings from an equivalence class to one of its representations, and vice-versa. We call them morphisms.
Rep takes an abstract object (an equivalence class), such as '-3', and transforms it into one of its representations, for example '(0,3)'. Abs does the opposite, taking a representation such as '(3,10)', and mapping it into its abstract object, which is '-7'. Int.thy (Isabelle's implementation of integers) defines these as Rep_Integ and Abs_Integ for integers.
Notice that the statement '(2,3) = (8,9)' is an absurd. Since these are ordered pairs, that would imply '2 = 8' and '3 = 9'. On the other hand the statement 'Abs_Integ(2,3) = Abs_Integ(8,9)' is very much true, as we are simply saying that the integer abstraction of '(2,3)' is the same as the integer abstraction '(8,9)', namely '-1'.
A more precise phrasing of 'Abs_Integ(2,3) = Abs_Integ(8,9)' is: "'(2,3)' and '(8,9)' belong in the same equivalence class under the integer relation". We usually call this class '-1'.
It's important to note that '-1' is just a convenient shorthand for "the equivalence class of (0,1)", in the same vein that '5' is just a shorthand for "the equivalence class of (5,0)" and '-15' is shorthand for "the equivalence class of '(0,15)'. We call '(0,1)', '(5,0)', and '(0,15) the canonical representations. So saying "Abs_Integ(2,3) = -1" is really just a nice abbreviation for "Abs_Integ(2,3) = Abs_Integ(0,1)" .
It's also worth noting that the mapping Rep is one-to-one. This means that Rep_Integ(-1) will always yield the same representation pair, usually the canonical '(0,1)'. The specific pair picked does not matter much, but it'll always pick the same one. That is useful to know, as it implies that the statement Rep_Integ(i) = Rep_Integ(i) is always true.
The quotient_type command in Isabelle
'quotient_type' creates a quotient type using the specified type and equivalence relation. So quotient_type int = "nat × nat" / "intrel" creates the quotient type int, as the equivalence classes of nat × nat under the relation intrel (where "intrel = (λ(a,b) (x,y). a+y = x+b)"). Section 11.9.1 of the manual details the specifics about the command.
It's worth noting that you actually have to prove that the relation provided (intrel) is an equivalence.
Here's a usage example from Int.thy, which defines the integers, it's morphisms, and proves that intrel is an equivalence relation:
(* Definition *)
quotient_type int = "nat × nat" / "intrel"
morphisms Rep_Integ Abs_Integ
(* Proof that 'intrel' is indeed an equivalence *)
proof (rule equivpI)
show "reflp intrel" by (auto simp: reflp_def)
show "symp intrel" by (auto simp: symp_def)
show "transp intrel" by (auto simp: transp_def)
qed
Definitions and Lemmas: The Lifting and Transfer packages
Now, the previous explanations suggest that Rep and Abs should appear everywhere, right? These transformations are crucial for proving properties about quotient types. However, they appear less than 10 times throughout the 2000 lines of Int.thy. Why?
lift_definition and the proof method transfer are the answer. They come from the Lifting and Transfer packages. These packages do a lot, but for our purposes, they do the job of concealing Rep and Abs from your definitions and theorems.
The gist when working with quotient types in Isabelle, is that you want to [1] define some operations, [2] prove some useful lemmas with the representation type, and then [3] completely forget about these representations, working only with the abstract type. When proving theorems about the abstract type, you should be using the previously shown properties and lemmas.
To get [1], lift_definition helps you to define the operations. In specific, it allows you to define a function with the representation type, and it automatically "lifts" it to the abstract type.
As an example, you can define addition on integers as such:
lift_definition int_plus:: "int ⇒ int ⇒ int"
is "λ(a,b)(c,d). (a+c, b+d)"
This definition is stated in terms of nat × nat ⇒ nat × nat ⇒ nat × nat, but 'lift_definition' will automatically "lift" it to int ⇒ int ⇒ int.
An important thing to note is that you have to prove the function still follows the equivalence relation after applied (i.e. if 'x ≃ y' then 'f x ≃ f y'). The definition above for example, will prompt you to prove that "if '(a,b) ≃ (x,y)' and '(c,d) ≃ (u,v)', then '(a+c,b+d) ≃ (x+u,y+v)'" (if it doesn't look like it, try using apply clarify).
One of the nice things about lift_definition is that it works in terms of the underlying representation only, so you don't have to worry about transitioning between abstractions and representations. Hence the lack of Rep_Integ and Abs_Integ in Int.thy.
It also sets up a transfer rule for the function. This how you get [2]: proving properties without having to worry about Rep and Abs. Using the transfer proof method, you can bring a lemma about an abstraction down to the representation level, and prove the desired property there.
As an example, you can state the commutativity of addition in the form int_plus x y = int_plus y x, and then use the transfer method to bring that statement down to the representation level, which after a clarify looks like intrel (a + c, b + d) (c + a, d + b). We can then prove by simplification with the definition of intrel:
lemma plus_comm: "int_plus x y = int_plus y x"
apply transfer
apply clarify
by (simp add: intrel_def)
And to get [3], you simply use these lemmas and properties of the abstract type, without worrying about the actual representations.
After this point, you'll even forget that you're using a quotient type, since the abstract type and it's properties are all you need. Usually a handful of lemmas on the abstract type is enough, and Int.thy will give you a lot more than a handful.
References and further reading
Section 1 of the paper "Quotient Types" gives a good overview of the topic (and goes in depth in the other sections).
The introduction of "Quotients Revisited for Isabelle/HOL" also explains very well the purpose of 'Rep' and 'Abs'.
"Lifting and Transfer" is also a great read into how these can be concealed and the automation behind quotient types in Isabelle.
Isabelle's Reference Manual (with some ctrl+f) is also a great source when in doubt about what specific commands do.

How to replace ⋀ and ⟹ with ∀ and ⟶ in assumption

I'm an Isabelle newbie, and I'm a little (actually, a lot) confused about the relationship between ⋀ and ∀, and between ⟹ and ⟶.
I have the following goal (which is a highly simplified version of something that I've ended up with in a real proof):
⟦⋀x. P x ⟹ P z; P y⟧ ⟹ P z
which I want to prove by specialising x with y to get ⟦P y ⟹ P z; P y⟧ ⟹ P z, and then using modus ponens. This works for proving the very similar-looking:
⟦∀x. P x ⟶ P z; P y⟧ ⟹ P z
but I can't get it to work for the goal above.
Is there a way of converting the former goal into the latter? If not, is this because they are logically different statements, in which case can someone help me understand the difference?
That the two premises !!x. P x ==> P y and ALL x. P x --> P y are logically equivalent can be shown by the following proof
lemma
"(⋀x. P x ⟹ P y) ≡ (Trueprop (∀x. P x ⟶ P y))"
by (simp add: atomize_imp atomize_all)
When I tried the same kind of reasoning for your example proof I ran into a problem however. I intended to do the following proof
lemma
"⟦⋀x. P x ⟹ P z; P y⟧ ⟹ P z"
apply (subst (asm) atomize_imp)
apply (unfold atomize_all)
apply (drule spec [of _ y])
apply (erule rev_mp)
apply assumption
done
but at unfold atomize_all I get
Failed to apply proof method:
When trying to explicitly instantiate the lemma I get a more clear error message, i.e.,
apply (unfold atomize_all [of "λx. P x ⟶ P z"])
yields
Type unification failed: Variable 'a::{} not of sort type
This I find strange, since as far as I know every type variable should be of sort type. We can solve this issue by adding an explicit sort constraint:
lemma
"⟦⋀x::_::type. P x ⟹ P z; P y⟧ ⟹ P z"
Then the proof works as shown above.
Cutting a long story short. I usually work with Isar structured proofs instead of apply scripts. Then such issues are often avoided. For your statement I would actually do
lemma
"⟦⋀x. P x ⟹ P z; P y⟧ ⟹ P z"
proof -
assume *: "⋀x. P x ⟹ P z"
and **: "P y"
from * [OF **] show ?thesis .
qed
Or maybe more idiomatic
lemma
assumes *: "⋀x. P x ⟹ P z"
and **: "P y"
shows "P z"
using * [OF **] .
C.Sternagel answered your title question "How?", which satisfied your last sentence, but I go ahead and fill in some details based on his answer, to try to "help [you] understand the difference".
It can be confusing that there is ==> and -->, meta-implication and HOL-implication, and that they both have the properties of logical implication. (I don't say much about !! and !, meta-all and HOL-all, because what's said about ==> and --> can be mostly be transferred to them.)
(NOTE: I convert graphical characters to equivalent ASCII when I can, to make sure they display correctly in all browsers.)
First, I give some references:
[1] Isabelle/Isar Reference manual.
[2] HOL/HOL.thy
[3] Logic in Computer Science, by Huth and Ryan
[4] Wiki sequent entry.
[5] Wiki intuitionistic logic entry.
If you understand a few basics, there's nothing that confusing about the fact that there is both ==> and -->. Much of the confusion departs, and what's left is just the work of digging through the details about what particular source statements mean, such as the formula of C.Sternagel's first lemma.
"(!!x. P x ==> P y) == (Trueprop (!x. P x --> P y))"
C.Sternagel stopped taking the time to give me important answers, but the formula he gives you above is similar to one he gave me a while ago, to convince me that all free variables in a formula are universally quantified.
Short answer: The difference between ==> and --> is that ==> (somewhat) plays the part of the turnstile symbol, |-, of a non-generalized sequent in which there is only one conclusion on the right-hand side. That is, ==>, the meta-logic implication operator of Isabelle/Pure, is used to define the Isabelle/HOL implication object-logic operator -->, as shown by impI in the following axiomatization in HOL.thy [2].
(*line 56*)
typedecl bool
judgment
Trueprop :: "bool => prop"
(*line 166*)
axiomatization where
impI: "(P ==> Q) ==> P-->Q" and
mp: "[| P-->Q; P |] ==> Q" and
iff: "(P-->Q) --> (Q-->P) --> (P=Q)" and
True_or_False: "(P=True) | (P=False)"
Above, I show the definition of three other axioms: mp (modus ponuns), iff, and True_or_False (law of excluded middle). I do that to repeatedly show how ==> is used to define the axioms and operators of the HOL logic. I also threw in the judgement to show that some of the sequent vocabulary is used in the language Isar.
I also show the axiom True_or_False to show that the Isabelle/HOL logic has an axiom which Isabelle/Pure doesn't have, the law of excluded middle [5]. This is huge in answering your question "what is the difference?"
It was a recent answer by A.Lochbihler that finally gave meaning, for me, to "intuitionistic" [5]. I had repeatedly seen "intuitionistic" in the Isabelle literature, but it didn't sink in.
If you can understand the differences in the next source, then you can see that there's a big difference between ==> and -->, and between types prop and bool, where prop is the type of meta-logic propositions, as opposed to bool, which is the type of the HOL logic proposition. In the HOL object-logic, False implies any proposition Q::bool. However, False::bool doesn't imply any proposition Q::prop.
The type prop is a big part of the meta-logic team !!, ==>, and ==.
theorem "(!!P. P::bool) == Trueprop (False::bool)"
by(rule equal_intr_rule, auto)
theorem HOL_False_meta_implies_any_prop_Q:
"(!!P. P::bool) ==> PROP Q"
(*Currently, trying by(auto) will hang my machine due to blast, which is know
to be a problem, and supposedly is fixed in the current repository. With
`Auto methods` on in the options, it tries `auto`, thus it will hang it.*)
oops
theorem HOL_False_meta_implies_any_bool_Q:
"(!!P. P::bool) ==> Q::bool"
by(rule meta_allE)
theorem HOL_False_obj_implies_any_bool_Q:
"(!P. P::bool) --> Q::bool"
by(auto)
When you understand that Isabelle/Pure meta-logic ==> is used to define the HOL logic, and other differences, such as that the meta-logic is weaker because of no excluded middle, then you understand that there are significant differences between the meta-operators, !!, ==>, and ==, in comparison to the HOL object-logic operators, !, -->, and =.
From here, I put in more details, partly to convince any expert that I'm not totally abusing the word sequent, where my use here is based primarily on how it's used in reference [3, Huth and Ryan].
Attempting to not write a book
I throw in some quotes and references to show that there's a relationship between sequents and ==>.
From my research, I can't see that the word "sequent" is standardized. As far as I can tell, in [3.pg 5], Huth and Ryan use "sequent" to mean a sequent which has only has one conclusion on the right-hand side.
...This intention we denote by
phi1, phi2, ..., phiN |- psi
This expression is called a sequent; it is valid if a proof can be found.
A more narrow definition of sequent, in which the right-hand side has only one conclusion, matches up very nicely with the use of ==>.
We can blame L.Paulson for confusing us by separating the meta-logic from the object-logic, though we can thank him for giving us a larger logical playground.
Maybe to keep from clashing with the common definition of a sequent, as in [4, Wiki], he uses the phrase natural deduction sequent calculus in various places in the literature. In any case, the use of ==> is completely related to implementing natural deduction rules in the logic of Isabelle/HOL.
Even with generalized sequents, L.Paulson prefers the ==> notation:
Logic and Proof course 2012-13
Course materials: see slides for his generalized sequent calculus notation
You asked about differences. I throw in some source related to C.Sternagel's answer, along with the impI axiomatization again:
(*line 166*)
axiomatization where
impI: "(P ==> Q) ==> P-->Q"
(*706*)
lemma --"atomize_all [atomize]:"
"(!!x. P x) == Trueprop (ALL x. P x)"
by(rule atomize_all)
(*715*)
lemma --"atomize_imp [atomize]:"
"(A ==> B) == Trueprop (A --> B)"
by(rule atomize_imp)
(*line 304*)
lemma --"allI:"
assumes "!!x::'a. P(x)"
shows "ALL x. P(x)"
by(auto simp only: assms allI
I put impI in structured proof format:
lemma impI_again:
assumes "P ==> Q"
shows "P --> Q"
by(simp add: assms)
Now, consider ==> to be the use of the sequent turnstile, and shows to be the sequent notation horizontal bar, then you have the following sequent:
P |- Q
-------
P --> Q
This is the natural deduction implication introduction rule, as the axiom name says, impI (Cornell Lecture 15).
The Big Guys have been on top of all of this for a long time. See [1, Section 2.1, page 27] for an overview of !!, ==>, and ==. In particular, it says
The Pure logic [38, 39] is an intuitionistic fragment of higher-order logic
[13]. In type-theoretic parlance, there are three levels of lambda-calculus with
corresponding arrows =>/!!/==>`...
One general significance of the statement is that in the use of Isabelle/HOL, you are using two logics, a meta-logic and an object-logic, where those two terms come from L.Paulson, and where "intuitionistic" is a key defining point of the meta-logic.
See also [1, Section 9.4.1, Simulating sequents by natural deduction, pg 206]. According to M.Wenzel on the IsaUsersList, L.Paulson wrote this section. On page 205, Paulson first takes the definition of a sequent to be the generalized definition. On page 206, he then shows how you can line up one type of sequent with the use of ==>, which is by negating every proposition on the right-hand side of a sequent, except for one of them.
That, by all appearances, is a horn clause, which I know nothing about.
It seems obvious to me that using ==> is the use of a limited form of sequents. In any event, that's how I think of it, and thinking that way has given me an understanding of the differences between ==> and -->, along with the fact that the meta-logic has no excluded middle.
If A.Lochbhiler wouldn't have pointed out the absence of an excluded middle, I wouldn't have seen an important difference of what's possible with ==>, and what's possible with -->.
Maybe C.Sternagel will start back again to give me some of his important answers.
Please pardon the long answer.
Others have already explained some of the reasons behind the difference between meta-logic and logic, but missed the simple tactic apply atomize:
lemma "⟦⋀(x::'a). P x ⟹ P z ; P y⟧ ⟹ P z"
apply atomize
which yields the goal:
⟦ ∀x. P x ⟶ P z; P y ⟧ ⟹ P z
as desired.
(The additional type constraint ⋀(x::'a) is required for the reasons mentioned by chris.)
There is a lot of text already, so just a few brief notes:
Isabelle/Pure is minimal-higher order logic with the main connectives ⋀ and ⟹ to lay out Natural Deduction rules in a declarative way. The system knows how to compose them by basic means, e.g. in Isar proofs, proof methods like rule, attributes like OF.
Isabelle/HOL is full higher-order logic, with the full set of predicate logic connectives, e.g. ∀ ∃ ∧ ∨ ¬ ⟶ ⟷, and much more library material. Canonical introduction rules like allI, allE, exI, exE etc. for these connectives explain formally how the reasoning works wrt. the Pure framework. HOL ∀ and ⟶ somehow correspond to Pure ⋀ and ⟹, but they are of different category and should not be thrown into the same box.
Note that apart from the basic thm command to print such theorems, it occasionally helps to use print_statement to get an Isar reading of these Natural Deduction reasoning forms.

Resources