Zulip Chat Archive
Stream: new members
Topic: Taxonomy
rzeta0 (Oct 22 2024 at 18:50):
Following a good but long discussion in another thread, I've written up a blog to briefly explain just enough (for beginners) about:
- universe, types and terms
- the initially odd idea that proofs are elements of propositions
- explaining how using hypotheses in proofs is actually using proofs of propositions
I'd welcome feedback / corrections: https://leanfirststeps.blogspot.com/2024/10/appendix-taxonomy.html
Kyle Miller (Oct 22 2024 at 18:54):
13 = 2*6+1
is a proposition, not a proof. rfl
is an example of a proof.
Arthur Paulino (Oct 22 2024 at 18:56):
Maybe this clarifies:
#check Eq 13 (2 * 6 + 1) -- 13 = 2 * 6 + 1 : Prop
Arthur Paulino (Oct 22 2024 at 18:59):
If you perform a "go to definition" on =
, it should take you to Notation.lean
from core, highlighting the following line:
macro_rules | `($x = $y) => `(binrel% Eq $x $y)
That is, a = b
is syntax sugar for Eq a b
For further investigation, it might be worth checking the definition of Eq
. The docstring is quite rich
Arthur Paulino (Oct 22 2024 at 19:10):
And this is a very raw example:
example : Eq 3 3 := Eq.refl 3
rzeta0 (Oct 22 2024 at 20:35):
I think this has made realise I don't actually know what a proof is, despite having written many of them in lean exercises!
Consider Odd 13
. This is a proposition, I think we can agree on this.
The following is a lean proof if this proposition:
import Mathlib.Tactic
example : Odd (13: ℤ) := by
use 6
norm_num
So my understanding that example
is the proof of the proposition Odd (13:ℤ)
, but example
is a special case where the name of the proof is discarded and cannot be referred to later.
In any case, the code after :=
is equivalent to the proof, so the proof is all the code use 6; norm_num
.
Here is another example:
import Mathlib.Tactic
example : IsSquare 9 := by
use 3
Here the proposition is IsSquare 9
, and the proof is use 3
. Is that a better simpler example of a proof?
So is it the case that many proofs are actually tactics?
I guess the core question I'm asking is "what are examples of proofs?" - and I realise this is an odd question to ask given I've been writing dozens of lean proofs as I do the Mechanics of Proof course.
Yakov Pechersky (Oct 22 2024 at 20:38):
So is it the case that many proofs are actually tactics?
No, tactics construct the term that is the "proof term". No, example
is not the proof of the proposition. Instead, example : T := term
is the command that tells Lean "I am about to provide a term of type T, please check that for me, and don't worry about remembering this fact"
Kyle Miller (Oct 22 2024 at 20:38):
The entire expression
by
use 6
norm_num
evaluates to a proof.
You can use by?
to see what proof it evaluates to. Don't expect to understand it! It's like trying to read assembly code from compiler output.
Yakov Pechersky (Oct 22 2024 at 20:40):
and it just so happens that one can construct a term using tactics because the by
keyword means "I am about to give you a term through constructing it using tactics".
You might be familiar with exact
. That is a keyword that says "I am about to give you a tactic that provides precisely the term to follow".
Yakov Pechersky (Oct 22 2024 at 20:40):
Note the dual nature between by
and exact
Kyle Miller (Oct 22 2024 at 20:40):
You can wonder philosophically about whether by use 6; norm_num
is the proof itself, but in any case, that (or what it evaluates to) is the proof, whose type is Odd (13: ℤ)
.
Kyle Miller (Oct 22 2024 at 20:41):
rzeta0 said:
"what are examples of proofs?"
Arthur has already given you an example of Eq.refl 3
as a proof.
Edward van de Meent (Oct 22 2024 at 20:41):
if you want to write it without tactics:
example : Odd (13: ℤ) :=
⟨6,rfl⟩
example : IsSquare 9 :=
⟨3,rfl⟩
(still cheating a bit rfl
is in this case not a tactic afaik, but in spirit it is)
Kyle Miller (Oct 22 2024 at 20:42):
What do you mean? rfl
-the-term is a theorem: https://github.com/leanprover/lean4/blob/master/src/Init/Prelude.lean#L294
Edward van de Meent (Oct 22 2024 at 20:43):
huh... i didn't know that...
Kyle Miller (Oct 22 2024 at 20:44):
(You can "go to definition" to be sure :-))
Kyle Miller (Oct 22 2024 at 20:45):
You might say there's some magic to rfl
though because the elaborator fills in the a
argument from the expected type, though that's not magic specific to rfl
.
Edward van de Meent (Oct 22 2024 at 20:45):
i was under the impression that there was more magic to it, i guess...
Yakov Pechersky (Oct 22 2024 at 20:48):
I think to be most explicit, one would use Exists.intro
explicitly
Damiano Testa (Oct 22 2024 at 20:50):
Honestly, I find proof terms that involve rfl
to be the hardest to parse. I would probably start by looking at a "chaining" proof, something like a < b -> b < c -> a < c
.
Kyle Miller (Oct 22 2024 at 20:52):
Yeah, rfl
can be a bit subtle. Anyway, @Exists.intro Nat (fun r : Nat => 9 = r * r) 3 (@Eq.refl Nat 9 : 9 = 3 * 3)
is a proof of IsSquare 9
, written out in detail.
example : IsSquare 9 :=
@Exists.intro Nat (fun r : Nat => 9 = r * r) 3 (@Eq.refl Nat 9 : 9 = 3 * 3)
Derek Rhodes (Oct 22 2024 at 20:52):
@rzeta0 (if I may interject, the book "theorem proving in lean" starts off without tactics and immediately gets down to the nitty gritty about what proofs are and then later shows how tactics fit in starting in chapter 5)
rzeta0 (Oct 22 2024 at 21:15):
Kyle Miller said:
The entire expression
by use 6 norm_num
evaluates to a proof.
This is helpful.
I don't know what rfl
or refl
is so will have to appreciate those later on my journey.
Kyle Miller (Oct 22 2024 at 21:27):
Why wait? Here: Eq.refl a
is the proof that a = a
Arthur Paulino (Oct 22 2024 at 21:30):
@rzeta0 I can provide the explanation I believe I would need if I were trying to understand this a while ago.
It would be simpler if I could just say that a proof is a term of a type, but it sounds strange when I say that 3
is a proof of Nat
. So a more conservative approach is to say that a proof is a term of a type when this type inhabits Prop
. It may still be debatable, but definitely workable. So let's take it from there.
The community has absorbed the terminology "term mode" and "tactic mode" to differentiate whether you're using the tactic DSL or not.
For example, when I say example : Eq 3 3 := Eq.refl 3
, I'm literally calling the (only) constructor of the Eq
type. In a way, it's similar to example : Nat := Nat.zero
, but the Nat
type is not parametric.
If you choose to "enter tactic mode" by starting your proof with by
, it will tell Lean to spin a specific elaboration process, which is ultimately meant to produce a term of the type you're intending to inhabit. Or in other words, the type you're trying to come up with a term for.
This is not common, but take a look nevertheless because it may be strangely insightful:
def three : Nat := 3
def three' : Nat := by exact 3
Arthur Paulino (Oct 22 2024 at 21:39):
It may be the case that the way Lean 4 code is generally written is a bit overpowered when we're trying to get to a very detailed understanding of how things work.
One path is to just go with it, start your proof with by
, live in tactic mode and get your goals closed. This is definitely a valid path for math students whose job is to actually learn and practice mathematics. But once you write that by
, you're summoning a very powerful system. And at that point it's harder to point your finger at what is what.
rzeta0 (Oct 22 2024 at 21:53):
Two last questions on this.
Question 1
Consider h: Odd 13
.
I was told in the other thread that h
is a proof. So why is h
a proof but example
not?
Question 2
What's the difference between "is a proof" and "evaluates to a proof" ?
rzeta0 (Oct 22 2024 at 21:58):
@Arthur Paulino - thanks for trying hard to help. Sadly I'm still a beginner and so I didn't fully understand your examples. Maybe I will in a few weeks/months time.
In particular I wanted to understand your three
and three'
examples but I am not seeing the key lesson. Is the idea that by exact
will eventual return something that is equivalent to 3
?
Ruben Van de Velde (Oct 22 2024 at 21:58):
So, if you have
example : IsSquare 9 :=
@Exists.intro Nat (fun r : Nat => 9 = r * r) 3 (@Eq.refl Nat 9 : 9 = 3 * 3)
then probably one would not say that the example is a proof, but @Exists.intro Nat (fun r : Nat => 9 = r * r) 3 (@Eq.refl Nat 9 : 9 = 3 * 3)
is.
Say you are in the middle of a proof, and you need this fact, you can write
have H : IsSquare 9 := [... proof ...]
At this point, if you have a hypothesis h : IsSquare 9
, you could write in particular
have H : IsSquare 9 := h
or you could reuse the proof of the example:
have H : IsSquare 9 := @Exists.intro Nat (fun r : Nat => 9 = r * r) 3 (@Eq.refl Nat 9 : 9 = 3 * 3)
but you could not write
have H : IsSquare 9 := example ...
Ruben Van de Velde (Oct 22 2024 at 21:59):
Does any of that make sense?
Arthur Paulino (Oct 22 2024 at 22:06):
To the question you asked me, yes. That's the idea. by exact 3
is taking a slightly longer path but it gets elaborated to 3 in the end.
Ruben Van de Velde (Oct 22 2024 at 22:08):
Re three
and three'
- at a basic level, lean deals in terms of types. 3
is a term of type Nat
. Tactics - that is, everything after by
- are simply a way to build terms. In particular, the exact
tactic just creates the term that you write after exact
.
Maybe this can help:
def f (x : Nat) : Nat := x + 1
def g (x : Nat) : Nat := f (f x)
#print g
-- def g : Nat → Nat :=
-- fun x => f (f x)
def g' (x : Nat) : Nat := by
apply f
apply f
exact x
#print g'
-- def g' : Nat → Nat :=
-- fun x => f (f x)
This shows two ways of defining a function - the first (g
) just has a term after the :=
; the second (g'
) has a sequence of tactics which build up the function step by step - but in the end the two functions are exactly the same, as the #print
command shows
rzeta0 (Oct 22 2024 at 22:09):
@Ruben Van de Velde so in
have H : IsSquare 9 := h
h
is a proof. Not just because it looks and acts like one, but also because it is a term of the type proposition IsSquare 9
.
Am i correct up to this point.
If yes, then let's compare:
h : IsSquare 9
example : IsSquare 9
my_lemma : IsSquare 9
Why is h
not like my_lemma
?
Arthur Paulino (Oct 22 2024 at 22:13):
Oh, that's an interesting confusion. "example" is more like "def" or "theorem". It's syntax for a different purpose
Ruben Van de Velde (Oct 22 2024 at 22:13):
Well, each of those are a snippet of lean code. It might help to write them out:
import Mathlib
theorem x (h : IsSquare 9) : True := by
-----------^ this is the term
#check h
trivial
example : IsSquare 9 := ⟨3, rfl⟩
------------------------^^^^^^^^ this is the term
theorem isSquare_nine : IsSquare 9 := ⟨3, rfl⟩
--------------------------------------^^^^^^^^ this is the term
--------^^^^^^^^^^^^^ and now this is also a term of the same type:
#check isSquare_nine
Kyle Miller (Oct 22 2024 at 22:16):
Mario explained example
here for what it's worth.
rzeta0 (Oct 22 2024 at 22:24):
@Ruben Van de Velde thank you, your examples have helped a lot.
Thanks also @Kyle Miller a re-reading of Mario's replies helped.
Damiano Testa (Oct 22 2024 at 22:25):
import Mathlib
example : IsSquare 9 := ⟨3, rfl⟩
------------------------^^^^^^^^ this is the term
-------^ the margin of the syntax is too small to store the proof
rzeta0 (Oct 23 2024 at 08:03):
I've updated the blog to reflect the feedback here.
The only thing niggling at the back of my mind is the distinction between:
h
andby use 6; norm_num
are proofs ofOdd 13
h
andby use 6; norm_num
evaluate to proofs ofOdd 13
Is this a bigger issue than I think it is? Should I be making a distinction?
Arthur Paulino (Oct 23 2024 at 08:21):
If we go by what I said above:
So a more conservative approach is to say that a proof is a term of a type when this type inhabits
Prop
.
Then I would say that h
is a proof and by use 6; norm_num
evaluates to a proof.
Note that elaboration is not context-free so something you write with the tactics DSL may or may not prove a theorem.
def forty_two := 42
example : forty_two = 42 := by simp -- simp made no progress
@[simp] def forty_two' := 42
theorem foo : forty_two' = 42 := by simp -- success
#print foo -- theorem foo : forty_two' = 42 := of_eq_true (eq_self 42)
example : forty_two = 42 := of_eq_true (eq_self 42) -- success
We can't just say that by simp
is a proof of forty_two = 42
. But when I tagged the definition of forty_two'
with @[simp]
, it was added to the search space of the simp
tactic, allowing it to produce a proof of forty_two' = 42
for me.
And notably, the same proof worked for forty_two = 42
.
Arthur Paulino (Oct 23 2024 at 08:29):
But look, the term "proof" can be highly conflating, as it would be annoying and unnecessarily pedantic to fight the natural use of the word.
If a professor tells the students to write a proof of a theorem and they use the tactics DSL, everyone will be happy and that's totally fine. Although you can argue that Lean 4 was probably the one putting the pieces together and creating the proof term.
Arthur Paulino (Oct 23 2024 at 08:38):
If you can't make peace with imprecision, then you can just use the verb "prove the following theorem(s)" and the students will join forces with Lean 4 to produce the proof(s)
Last updated: May 02 2025 at 03:31 UTC