Zulip Chat Archive
Stream: lean4
Topic: propext docstring contains invalid code
Snir Broshi (Jul 23 2025 at 16:41):
propext (propositional extensionality) is in Init/Core.lean, documented here
Its docstring contains the following example:
set_option pp.proofs true
def foo : Nat := by
have : (True → True) ↔ True := ⟨λ _ => trivial, λ _ _ => trivial⟩
have := propext this ▸ (2 : Nat)
exact this
#reduce foo
-- propext { mp := fun x x => True.intro, mpr := fun x => True.intro } ▸ 2
#eval foo -- 2
This code fails, as you can see on Lean 4 Web, with the following error:
invalid `▸` notation, the equality
propext this
has type
(True → True) = True
but neither side of the equality is mentioned in the type
Nat
What goes wrong in this example? Is it something that broke in the Lean 4 transition?
Robin Arnez (Jul 23 2025 at 16:49):
My guess is that it previously did not complain about this exact problem and just did:
set_option pp.proofs true
def foo : Nat := by
have : (True → True) ↔ True := ⟨λ _ => trivial, λ _ _ => trivial⟩
have := Eq.rec (motive := fun _ _ => Nat) (2 : Nat) (propext this)
exact this
#reduce foo
-- foo._proof_2 foo._proof_1 ▸ 2
#eval foo -- 2
Snir Broshi (Jul 23 2025 at 17:15):
So should we fix it to use your solution in a PR?
François G. Dorais (Jul 24 2025 at 02:00):
What is this example supposed to illustrate?
Robin Arnez (Jul 24 2025 at 06:52):
Reduction getting stuck in the presence of this axiom
François G. Dorais (Jul 24 2025 at 11:31):
Doesn't reduction get stuck on any axiom? Anyway, it's an unusual thing to put in a docstring.
Snir Broshi (Jul 24 2025 at 11:47):
François G. Dorais said:
Doesn't reduction get stuck on any axiom?
Other than propext, the only axioms in Lean & Mathlib are Classical.choice and Quot stuff.
Here's an example with choice:
def foo : Set Nat := { x | x = 0 }
theorem foo_ne : Nonempty foo := Nonempty.intro ⟨0, rfl⟩
#reduce (Classical.choice foo_ne).val
-- (Classical.choice foo_ne).1
But can Quot.sound do this as well? Not sure
Robin Arnez (Jul 24 2025 at 12:25):
Quot.sound example:
structure TestType (x : Quot fun _ _ : Nat => True) where
value : Nat
def test : Nat :=
let a : TestType (Quot.mk _ 0) := ⟨3⟩
have h : Quot.mk (fun _ _ : Nat => True) 0 = Quot.mk _ 1 := Quot.sound trivial
(h ▸ a).value
#eval test
#reduce test
My guess is that propext is here in particular because it's commonly found when using rw. Maybe a better example is:
def myProp := ¬False
instance : Decidable myProp := by
simp [myProp]
infer_instance
#reduce decide myProp -- gets stuck
François G. Dorais (Jul 24 2025 at 12:26):
Snir said:
Other than
propext, the only axioms in Lean & Mathlib areClassical.choiceandQuotstuff.
And sorryAx, ofReduceBool, ofReduceNat, maybe a couple more? Also, opaque defs should break reduction in the same way. It seems to be there isn't anything special to propext here.
François G. Dorais (Jul 24 2025 at 23:25):
IMHO the bottom line is that this kind of reduction issue should be documented somewhere but the docstring for propext is not the right place since it gives little guidance on how to use propext other than the fact that it is an axiom.
Snir Broshi (Jul 25 2025 at 00:14):
So PR to delete it then?
François G. Dorais (Jul 25 2025 at 01:16):
That's probably the best.
Kyle Miller (Jul 25 2025 at 01:46):
I think the motivation of this part of the docstring is to point out that you can't use iffs to do rewrites and expect the result to still reduce. This is a reason for the overly general advice "don't use tactics to make definitions".
@Mario Carneiro wrote this — maybe he wants to iterate on it? Or maybe @David Thrane Christiansen is planning on incorporating it into the language reference?
Mario Carneiro (Jul 25 2025 at 01:48):
I'm not sure I understand the issue
Mario Carneiro (Jul 25 2025 at 01:48):
the docstring does mention that the same thing is true for any axiom
Mario Carneiro (Jul 25 2025 at 01:49):
the way to use propext is to prove that two propositions are equal given that they are iff, this seems self-explanatory and is also the first paragraph
Kyle Miller (Jul 25 2025 at 01:51):
The issue is that the example doesn't work right now. Mainly, I figured I'd ping you before someone makes a PR to delete anything, and if someone's wanting to modify things, best to ping the original author.
Mario Carneiro (Jul 25 2025 at 01:52):
this code was definitely tested at one point, so it must be a regression
Kyle Miller (Jul 25 2025 at 01:54):
I'm sure you tested it. I think what happened is the triangle operator was improved. It notices that neither True nor True -> True appears in Nat and fails intentionally.
Mario Carneiro (Jul 25 2025 at 01:55):
here's a fixed version, but it has significantly degraded as a demonstration:
set_option pp.proofs true
def foo : Nat := by
have : (True → True) ↔ True := ⟨λ _ => trivial, λ _ _ => trivial⟩
have : Nat := (propext this).recOn (motive := λ _ _ => Nat) 2
exact this
#reduce foo
-- foo._proof_2 foo._proof_1 ▸ 2
#eval foo -- 2
Mario Carneiro (Jul 25 2025 at 01:56):
maybe there is a way to disable the proof extraction
David Thrane Christiansen (Jul 25 2025 at 21:02):
I think the docstring should just link to the section on reduction and axioms if this is something that needs to be explicitly called out here, and this example can be deleted.
David Thrane Christiansen (Jul 25 2025 at 21:02):
I can do it in the next couple of days
Robin Arnez (Jul 25 2025 at 21:51):
There's some stuff in that section that's wrong/misleading, specifically the "Axioms and Compilation" section:
The function is marked
partialto avoid further error messages from termination checking.
That's not true, there are no further error messages.
The by_cases tactic uses the Law of the Excluded Middle, which is proved in the standard library using the Axiom of Choice. Indeed, this definition uses three axioms:
This is misleading, the actual source of Classical.choice turns out to be Nat.left_eq_add! In fact, this variant doesn't use choice:
def nextOdd (k : Nat) :
{ n : Nat // n % 2 = 1 ∧ (n = k ∨ n = k + 1) } where
val := if k % 2 = 1 then k else k + 1
property := by
by_cases k % 2 = 1 <;>
simp only [↓reduceIte, Nat.succ_ne_self, Nat.ne_add_one, or_false, or_true, and_true, *] <;> omega
Mario Carneiro (Jul 26 2025 at 22:42):
Man, this thing about stupid uses of choice bums me out so much. Lean FRO has changed a lot in the past few years, I'd really like us to allow some PRs through to fix the low hanging fruit here
David Thrane Christiansen (Jul 28 2025 at 15:28):
Thanks @Robin Arnez, I've fixed the inaccuracies.
Last updated: Dec 20 2025 at 21:32 UTC