Zulip Chat Archive
Stream: general
Topic: Best practice for numeral lemmas
Yaël Dillies (Oct 10 2025 at 14:44):
It used to be the case that numerals in Lean went 0, 1, bigger. That meant that, whenever proving a lemma involving a natural number n : ℕ coerced to eg a ring, one would need to prove three additional lemmas: one for 0, one for 1, one for ofNat(n) where (n : ℕ) [n.AtLeastTwo]. But as of recently, it seems that all three cases can be dealt at once with the new docs#Semiring.toGrindSemiring_ofNat instance. Is this intended?
Kenny Lau (Oct 10 2025 at 14:49):
Yaël Dillies said:
all three cases can be dealt at once
but grind is not the universal tactic
Yaël Dillies (Oct 10 2025 at 14:54):
What? This has nothing to do with grind; I am talking about rewriting tactics. Consider:
import Mathlib.Algebra.Ring.Defs
variable (R : Type) [Semiring R]
def foo (n : ℕ) : R := 0
-- This lemma used not to be stateable because of the lack
-- of a "generic" `ofNat` instance.
@[simp] lemma foo_ofNat (n : ℕ) : foo R ofNat(n) = 0 := rfl
-- These used to be the three cases one would need to prove.
example : foo R 0 = 0 := by simp
example : foo R 1 = 0 := by simp
example (n : ℕ) [n.AtLeastTwo] : foo R ofNat(n) = 0 := by simp
Kenny Lau (Oct 10 2025 at 14:55):
interesting, I just thought this is grind because the name contains grind
Yaël Dillies (Oct 10 2025 at 14:56):
The instance was introduced to be used in grind internals, correct, but the connection stops there.
Yury G. Kudryashov (Oct 10 2025 at 15:13):
The instance was introduced by @Kim Morrison , see docs#Semiring.toGrindSemiring, to make grind work with Mathlib's (semi)rings.
Yury G. Kudryashov (Oct 10 2025 at 15:16):
I see 2 ways to go forward with it in a consistent way:
- introduce an instance for
OfNatassuming[NatCast R]; we have it for semirings anyway. - restrict the
grind-related instances (incl.GrindSemiring->OfNat) to some scope and makegrindopen this scope.
Aaron Liu (Oct 10 2025 at 16:57):
Maybe we should bundle an ofNat field into AddMonoidWithOne
Yury G. Kudryashov (Oct 10 2025 at 18:28):
I see no reason to have both NatCast R and \forall n, OfNat R n.
Aaron Liu (Oct 10 2025 at 18:49):
It's to fix definitional equalities
Aaron Liu (Oct 10 2025 at 18:51):
since we have a Zero R which gives OfNat R 0 and we have a One R which gives OfNat R 1 and of course we want the NatCast R to be something as well
Yury G. Kudryashov (Oct 10 2025 at 18:52):
I see not issues with OfNat being | 0 => 0 | 1 => 1 | n + 2 => Nat.cast (n + 2). It isn't defeq to Nat.cast n for a generic n, but it is defeq for any specific n.
Yury G. Kudryashov (Oct 10 2025 at 18:52):
(that's what the grind instance uses)
Aaron Liu (Oct 10 2025 at 18:53):
not defeq to the ofnat nat instance
Aaron Liu (Oct 10 2025 at 18:53):
Aaron Liu (Oct 10 2025 at 18:54):
for generic n
Yury G. Kudryashov (Oct 10 2025 at 18:54):
It is for any specific number.
Aaron Liu (Oct 10 2025 at 18:54):
for specific n it is defeq I guess
Yury G. Kudryashov (Oct 10 2025 at 18:55):
CC: @Kim Morrison @Mario Carneiro
Eric Wieser (Oct 10 2025 at 21:44):
Doesn't this match mean that [n.AtLeastTwo] : (ofNat(n) : R) = Nat.cast ofNat(n) is no longer rfl?
Aaron Liu (Oct 10 2025 at 21:52):
were we ever worried about OfNat.ofNat being defeq to Nat.cast
Eric Wieser (Oct 10 2025 at 21:53):
We certainly cared about the special case (Nat.cast 0 : Fin n) = 0 in the past
Eric Wieser (Oct 10 2025 at 21:54):
I think in the end we might have redefined % to make that true
Aaron Liu (Oct 10 2025 at 22:11):
I'm still a bit annoyed at Nat.ble 0 x = true is not defeq
Yury G. Kudryashov (Oct 11 2025 at 00:34):
Eric Wieser said:
Doesn't this match mean that
[n.AtLeastTwo] : (ofNat(n) : R) = Nat.cast ofNat(n)is no longer rfl?
It will be rfl for all literal ns, but not for a generic n.
Yury G. Kudryashov (Oct 11 2025 at 00:35):
This will change some proofs about OfNat.ofNat that rely on defeq.
Eric Wieser (Oct 11 2025 at 06:26):
It seems slightly silly for the above not to hold for generic n even when R := Nat
Last updated: Dec 20 2025 at 21:32 UTC