Zulip Chat Archive
Stream: new members
Topic: get the witness of `β!`?
Yusuke Inoue (Jul 17 2025 at 15:54):
In first order logic, when we have a β! prop, we can define a new constant representing the only witness of prop.
Logically, I think it could be done in Lean, but actually not.
Here are two ways I know and why I don't think they're good:
- using a type in
Typeinstead : that requires much stronger premise - using
Classical.choose: in FOL AC isn't needed to do that
The witness of a β! prop is actually 'subsingle', so maybe allow getting the witness from that a prop wouldn't lead to inconsistency?
Aaron Liu (Jul 17 2025 at 15:55):
If you allow eliminating out the witness such that elim (mk x proof) = x definitionally then it makes definitional equality undecidable
Aaron Liu (Jul 17 2025 at 15:56):
well, even more undecidable (it was undecidable already because of a similar problem with Acc.rec)
Aaron Liu (Jul 17 2025 at 16:01):
this is the same problem as #Type theory > Can `Squash.mk x = Squash.mk y` be made true by definition? @ π¬ but with ExistsUnique instead of Squash
Kyle Miller (Jul 17 2025 at 16:01):
My rough understanding here is that to be able to get the witness, you need to have some sort of computational interpretation of proofs for it all to work out (since you'll need to be able to compute with that witness in reductions; I think this touches on what Aaron is saying about undecidability).
There is a computational interpretation of proofs, but Lean tends to avoid doing any proof reductions if it can help it. The compiler will erase proofs for example, so you won't be able to use this for programs, without a lot of extra work to figure out how to modify the compiler.
But, the definitional equality problem that Aaron mentions is a problem. Given two witnesses x and y, proof irrelevance means mk x proof1 = mk y proof2 is a definitional equality, so elim (mk x proof1) = elim (mk y proof2) is a definitional equality, and so x = y is a definitional equality. That's not good, because x and y might not directly be definitionally equal (!). I'm not sure this is a very big problem, given that the definitional equality algorithm already isn't transitive, but it certainly won't make the situation better.
Yusuke Inoue (Jul 17 2025 at 16:05):
Could we simply drop the computational interpretation, making the witness opaque?
Yusuke Inoue (Jul 17 2025 at 16:06):
just like the way it works in FOL, imo
Kyle Miller (Jul 17 2025 at 16:06):
May as well use Classical.choose then I think?
Yusuke Inoue (Jul 17 2025 at 16:09):
eh, I think Classical.choose looks too strong for this purpose.Anyway, in FOL, AC is not needed to do that.
Kyle Miller (Jul 17 2025 at 16:09):
I believe in type theory "getting the witness from a proof" usually means something like "reduce the proof to weak-head normal form ExistsUnique.mk x proof and then use x". Anything else amounts to adding an axiom, where you don't "really" get a witness, just a purported one that's supposed to exist.
Kyle Miller (Jul 17 2025 at 16:11):
My understanding is that FOL doesn't have the same sense of constructions as in the Calculus of Constructions. It's a much more stringent requirement to be able to "get" the witness in CoC than FOL, since "getting" the witness means you need to be able to compute with the witness in some way.
Kyle Miller (Jul 17 2025 at 16:12):
In CoC, you can "get" the witness of even an existential in a more limited sense, using Exists.rec, and that's probably similar to what it means to get a witness in FOL. (I'm no logician though!)
Yusuke Inoue (Jul 17 2025 at 16:17):
Maybe introducing a way to get a witness can better simulate the feeling of doing FOL when actually using Lean, that's what I think when asking this question
Aaron Liu (Jul 17 2025 at 16:17):
How do you "get" the witness in FOL, when you're stating the theorem? That is, if I have a proof that β! x, P x how do I state that "Q x where x is the unique x such that P x" in FOL? Clearly you need some sort of quantification to contain the x, and then once you start proving that Q x you don't need any choice in the proof, you just unwrap the exists.
Aaron Liu (Jul 17 2025 at 16:18):
My point is that you can do this in Lean too, it just looks kind of ugly
Yusuke Inoue (Jul 17 2025 at 16:18):
Yes, but without a way to introduce a constant representing the witness, the sentences will easily be quite long.
Aaron Liu (Jul 17 2025 at 16:18):
this happens in FOL too
Yusuke Inoue (Jul 17 2025 at 16:19):
Aaron Liu said:
My point is that you can do this in Lean too, it just looks kind of ugly
Yes that's what I think.
suhr (Jul 17 2025 at 16:19):
Yusuke Inoue said:
Anyway, in FOL, AC is not needed to do that.
In FOL you just never leave the Prop.
Kyle Miller (Jul 17 2025 at 16:19):
I think it wouldn't be too hard of a metaprogramming exercise to make an elaborator that eliminates uses of Classical.choice in a Prop context.
Aaron Liu (Jul 17 2025 at 16:20):
the reason you don't need AC to do this in FOL is because in FOL you have these lengthy sentences that every time they need to reference the unique x that satisfies P they put a quantifier instead and you can do that in Lean too
Kyle Miller (Jul 17 2025 at 16:20):
@Yusuke Inoue Are you familiar with using obtain to get a witness from an existential? That's usually how people do it, rather than using Classical.choose, unless you mean you want to use it inside of other definitions.
Kyle Miller (Jul 17 2025 at 16:21):
I think it would be very useful to have a #mwe to ground this discussion at this point.
Yusuke Inoue (Jul 17 2025 at 16:24):
Aaron Liu said:
this happens in FOL too
I read somewhere that when we have a existsunique prop, introducing its witness as a constant is a conservative extension. Maybe in proof assistants based on FOL they allow users to do that (just guessing, so far I've never used them).
Yusuke Inoue (Jul 17 2025 at 16:25):
Kyle Miller said:
Yusuke Inoue Are you familiar with using
obtainto get a witness from an existential? That's usually how people do it, rather than usingClassical.choose, unless you mean you want to use it inside of other definitions.
Yes I know obtain
Yusuke Inoue (Jul 17 2025 at 16:27):
Kyle Miller said:
I think it would be very useful to have a #mwe to ground this discussion at this point.
Good idea. I'll give one
Terence Tao (Jul 17 2025 at 19:00):
Some relevant recent discussion: . As observed there, the ability to extract witnesses from an ExistUnique is equivalent to having a choice function for non-empty singletons.
I also wrote some basic API for extracting witnesses (using choice) at https://github.com/teorth/analysis/blob/main/analysis/Analysis/Tools/ExistsUnique.lean (this doesn't directly address your question, but helped with the practical concern of just extracting the constant for use in Lean proofs).
Yusuke Inoue (Jul 18 2025 at 04:32):
Yusuke Inoue said:
Kyle Miller said:
I think it would be very useful to have a #mwe to ground this discussion at this point.
Good idea. I'll give one
It's from Tao's analysis I.
Exercise 3.5.12. This exercise will establish a rigorous version of Proposition 2.1.16. Let be a function, and let be a natural number. Show that there exists a function such that
and furthermore that this function is unique.Hint: First show inductively that for every natural number , there exists a unique function such that:
For an additional challenge: Prove this using only Peano axioms (no ordering properties or Proposition 2.1.16).
Challenge Hint: First show inductively that for every , there exists a unique pair of subsets satisfying:
(a)
(b)
(c)
(d)
(e) If , then
(f) If and , thenThen use as in the previous argument.
import Mathlib
class PeanoAxioms (N : Type) extends Zero N where
succ : N -> N
zero_no_succ : βn : N, Β¬ succ n = 0
succ_cancel : βa b : N, succ a = succ b β a = b
induction : βf : N β Prop, (f 0 β§ βn : N, f n β f (succ n)) β βn : N, f n
postfix:100 "++" => PeanoAxioms.succ
namespace PeanoAxioms
variable {N : Type}[PeanoAxioms N]
lemma unique_set_up_to_n : βn, β!p : Set N Γ Set N ,let (a,b) := p;
a β© b = β
β§ a βͺ b = Set.univ β§ 0 β a β§ n++ β b β§
(βm, m β b β m++ β b) β§ (βm, (m β a β§ m β n) β m++ β a) := by sorry
lemma unique_fun : β!f : N β Set N Γ Set N,βn,let (a,b) := f n;
a β© b = β
β§ a βͺ b = Set.univ β§ 0 β a β§ n++ β b β§
(βm, m β b β m++ β b) β§ (βm, (m β a β§ m β n) β m++ β a) := by sorry;
def A (n : N) : Set N := (unique_fun |> ExistsUnique.exists |> Classical.choose) n |> Prod.fst
lemma unique_fn_for_n (f : N β N β N) (c : N) : βn,β!a : {m // A n m} β N,
(βh : 0 β A n, a β¨0,hβ© = c) β§ βm : {m : N // A n m }, βm β n β βh : (βm)++ β A n, a β¨(βm)++,hβ© = f (βm) (a m) := by sorry
Without defining A here, the unique_fn_for_n statement will be quite long.
Defining A using existing axioms in Mathlib requires Classical.choose, which seems too strong.
An additional unique_choiceΒ axiom looks appealing to me :upside_down:
Kyle Miller (Jul 18 2025 at 13:45):
Maybe a unique_choice axiom would be a weaker assumption (at the cost of adding a new axiom!) but practically it's usually best to avoid any kind of choice at all if it's possible to construct the objects under consideration.
Something that's missing from the Peano axioms is a recursion principle, which would let you construct these sets without any need for choice. Here it is mostly worked out for creating the sets a and b, minus the part that shows the sets are unique:
import Mathlib
class PeanoAxioms (N : Type) extends Zero N where
succ : N -> N
zero_no_succ : βn : N, Β¬ succ n = 0
succ_cancel : βa b : N, succ a = succ b β a = b
induction : βf : N β Prop, (f 0 β§ βn : N, f n β f (succ n)) β βn : N, f n
recurse : β (f : N β Type), (f 0) β ((n : N) β f n β f (succ n)) β (n : N) β f n
recurse_zero : β (f : N β Type) (z : f 0) (s : (n : N) β f n β f (succ n)),
recurse f z s 0 = z
recurse_succ : β (f : N β Type) (z : f 0) (s : (n : N) β f n β f (succ n)),
β (n : N), recurse f z s (succ n) = s n (recurse f z s n)
postfix:100 "++" => PeanoAxioms.succ
namespace PeanoAxioms
variable {N : Type} [PeanoAxioms N]
def A : N β Set N :=
PeanoAxioms.recurse
(fun _ => Set N)
{0}
(fun m last => insert (m++) last)
def B (n : N) : Set N := Set.univ \ A n
lemma cases (f : N β Prop) (zero : f 0) (succ : β n : N, f (succ n)) : βn : N, f n :=
PeanoAxioms.induction f β¨zero, fun n _ => succ nβ©
lemma succ_ne_self : β (n : N), Β¬ n++ = n := by
apply PeanoAxioms.induction
constructor
Β· exact PeanoAxioms.zero_no_succ 0
Β· intro n hn h
apply hn
apply PeanoAxioms.succ_cancel
exact h
lemma inter_eq_empty (n : N) : A n β© B n = β
:= by simp [A, B]
lemma union_eq_univ (n : N) : A n βͺ B n = Set.univ := by simp [A, B]
lemma zero_mem_a : β n : N, 0 β A n := by
apply PeanoAxioms.induction
constructor
Β· simp [A, PeanoAxioms.recurse_zero]
Β· intro n hn
rw [A] at hn
simp [A, PeanoAxioms.recurse_succ, hn]
lemma mem_of_succ_mem_a : β (n m : N), m++ β A n β m β A n := by
apply PeanoAxioms.induction
constructor
Β· simp [A, PeanoAxioms.recurse_zero]
intro m h
exact absurd h (PeanoAxioms.zero_no_succ m)
Β· intro n ih m
simp [A, PeanoAxioms.recurse_succ]
rintro (h | h)
Β· replace h := PeanoAxioms.succ_cancel _ _ h
subst h
right
revert m
apply PeanoAxioms.cases
Β· intro
simp [PeanoAxioms.recurse_zero]
Β· intro m _
simp [PeanoAxioms.recurse_succ]
Β· right
exact ih m h
lemma self_mem_a : β (n : N), n β A n := by
apply PeanoAxioms.cases
Β· simp [A, PeanoAxioms.recurse_zero]
Β· intro n
simp [A, PeanoAxioms.recurse_succ]
lemma succ_self_not_mem_a : β (n : N), Β¬ n++ β A n := by
apply PeanoAxioms.induction
constructor
Β· simp [A, PeanoAxioms.recurse_zero, zero_no_succ]
Β· intro n
apply mt
intro h
rw [A, PeanoAxioms.recurse_succ, β A] at h
simp [succ_ne_self] at h
revert h
apply mem_of_succ_mem_a
lemma succ_mem_a : β (n m : N), m β A n β§ m β n β m++ β A n := by
apply PeanoAxioms.induction
constructor
Β· simp [A, PeanoAxioms.recurse_zero]
Β· intro n ih
rintro m β¨h1, h2β©
revert h1
rw [A, PeanoAxioms.recurse_succ, β A]
simp [h2]
rw [or_iff_not_imp_left]
intro h3 h4
apply ih
refine β¨h3, ?_β©
contrapose! h4
rw [h4]
lemma succ_mem_b : β (n : N), n++ β B n := by
simp [B]
apply succ_self_not_mem_a
lemma succ_mem_b_of_mem_b (n m : N) : m β B n β m++ β B n := by
simp [B]
apply mt
apply mem_of_succ_mem_a
lemma unique_set_up_to_n : βn, β!p : Set N Γ Set N ,let (a,b) := p;
a β© b = β
β§ a βͺ b = Set.univ β§ 0 β a β§ n++ β b β§
(βm, m β b β m++ β b) β§ (βm, (m β a β§ m β n) β m++ β a) := by
intro n
use (A n, B n)
dsimp only
use! inter_eq_empty n, union_eq_univ n, zero_mem_a n, succ_mem_b n, succ_mem_b_of_mem_b n,
succ_mem_a n
rintro β¨a, bβ©
simp
sorry -- prove uniqueness
Kyle Miller (Jul 18 2025 at 13:52):
Kyle Miller said:
I think it wouldn't be too hard of a metaprogramming exercise to make an elaborator that eliminates uses of
Classical.choicein aPropcontext.
More details about this idea:
I'm imagining that you would be able to define A using Classical.choice, and then your proof would look like
lemma unique_set_up_to_n : βn, β!p : Set N Γ Set N ,let (a,b) := p;
a β© b = β
β§ a βͺ b = Set.univ β§ 0 β a β§ n++ β b β§
(βm, m β b β m++ β b) β§ (βm, (m β a β§ m β n) β m++ β a) :=
elim_choice% [A] by
... a proof that uses A ...
which would tell it to unfold A while trying to eliminate uses of Classical.choice, which it will be able to do because the goal is a proposition. This would simulate the conservative extension idea you mentioned @Yusuke Inoue, but restricted to a Prop context.
Yusuke Inoue (Jul 18 2025 at 13:54):
Kyle Miller said:
Maybe a
unique_choiceaxiom would be a weaker assumption (at the cost of adding a new axiom!) but practically it's usually best to avoid any kind of choice at all if it's possible to construct the objects under consideration.
I have just realized that Classical.EM is weaker than Classical.choicebut there's no additional axiom for it, the fact shows that usually 'the cost of adding a new axiom' is seen to be expensive.
Kyle Miller (Jul 18 2025 at 13:57):
Re this point about a missing recursion principle: it's the Calculus of Constructions, not the Calculus of Things We Proved That Exist :-)
Kyle Miller (Jul 18 2025 at 14:06):
I want to be clear that I'm not saying anything about whether it's worth studying which axioms are needed for what or not.
You're certainly free to add your own unique choice axiom!
If you want to be safe about it, you could define it as an opaque definition using Classical.choice, and then treat this as an axiom.
Yusuke Inoue (Jul 18 2025 at 14:16):
Thanks! I think I've got a lot from these replies.
Last updated: Dec 20 2025 at 21:32 UTC