Zulip Chat Archive
Stream: lean4
Topic: Why does decide fail after applying iff in this example?
Mario Weitzer (Jan 13 2026 at 12:36):
def f_pos_nz (f : Nat → Nat) (h : ∃ n, f n ≠ 0) := Nat.find h
def f_pos_nz' (f : Nat → Nat) (h : f ≠ 0) := Nat.find (Function.ne_iff.mp h)
example : f_pos_nz (λ n => n) ⟨1, Nat.one_ne_zero⟩ = 1 := by decide
-- Works fine.
example : f_pos_nz' (λ n => n) (Function.ne_iff.mpr ⟨1, Nat.one_ne_zero⟩) = 1 := by decide
/-
Tactic `decide` failed for proposition
f_pos_nz' (fun n ↦ n) ⋯ = 1
because its `Decidable` instance
instDecidableEqNat (f_pos_nz' (fun n ↦ n) ⋯) 1
did not reduce to `isTrue` or `isFalse`.
After unfolding the instances `instDecidableEqNat`, `Classical.propDecidable`, and `Nat.decEq`, reduction got stuck at the `Decidable` instance
match h : (f_pos_nz' (fun n ↦ n) ⋯).beq 1 with
| true => isTrue ⋯
| false => isFalse ⋯
-/
Ruben Van de Velde (Jan 13 2026 at 16:02):
Looks like there's a broken instance
Ruben Van de Velde (Jan 13 2026 at 16:06):
Not sure what it might be though
Eric Wieser (Jan 13 2026 at 16:48):
Nat.find is not reducible, I think?
Mario Weitzer (Jan 13 2026 at 17:10):
But why does it work in one case but not the other? And how can I make it work in both cases? Is there a reducible version of Nat.find?
Bbbbbbbbba (Jan 14 2026 at 04:06):
Maybe it is the same reason why Never mind, I guess this does not strip off any complexity...rfl could close this?
example : f_pos_nz (λ n => n) ⟨1, Nat.one_ne_zero⟩ = 1 := by
apply of_decide_eq_true
-- ⊢ decide (f_pos_nz (fun n ↦ n) ⋯ = 1) = true
rfl
Bbbbbbbbba (Jan 14 2026 at 04:17):
Well, this shows a difference:
example : f_pos_nz (λ n => n) ⟨1, Nat.one_ne_zero⟩ = 1 := by
apply of_decide_eq_true
conv => lhs; rhs; lhs; reduce -- 1
rfl
example : f_pos_nz' (λ n => n) (Function.ne_iff.mpr ⟨1, Nat.one_ne_zero⟩) = 1 := by
apply of_decide_eq_true
conv => lhs; rhs; lhs; reduce /-
(Acc.rec
(fun x₁ h ih al ↦
Decidable.rec (fun h ↦ ih x₁.succ ⋯ ⋯) (fun h ↦ ⟨x₁, ⋯⟩)
((fun {q} motive dq h_1 h_2 ↦ Decidable.rec (fun h ↦ h_2 h) (fun h ↦ h_1 h) dq)
(fun dp ↦ Decidable ¬(fun n ↦ n) x₁ = 0 x₁)
((fun motive x h_1 h_2 ↦ Bool.rec (motive := fun x_1 ↦ x = x_1 → motive x_1) h_2 h_1 x ⋯)
(fun x ↦ Decidable ((fun n ↦ n) x₁ = 0 x₁)) (x₁.beq Nat.zero) (fun h ↦ isTrue ⋯) fun h ↦ isFalse ⋯)
(fun hp ↦ isFalse ⋯) fun hp ↦ isTrue hp))
⋯ ⋯).1
-/
sorry
Bbbbbbbbba (Jan 14 2026 at 06:28):
Silly me, I just realized that the first example could already be closed by rfl, so this is more of a defeq thing than a decide thing
Mario Weitzer (Jan 14 2026 at 08:46):
I see, thanks for the explanations so far. But this leaves the question on how to make Nat.find reduce in these situations. Or is there another function I could use that would work here?
Bbbbbbbbba (Jan 14 2026 at 09:35):
My guess would be that it needs to see an explicit witness of existence (thus an upper bound). Can you always specify one in your use case?
Mario Weitzer (Jan 14 2026 at 10:03):
Bbbbbbbbba said:
My guess would be that it needs to see an explicit witness of existence (thus an upper bound). Can you always specify one in your use case?
No, I can't, but I still don't get why this is an issue. If I know that f n will be nonzero eventually, why can't I tell Lean to just keep looking no matter how long it takes? I find it frustrating to convince Lean that something is decidable only to find out that it will refuse to decide anyway. Is there no way to tell Lean that it should just decide based on the fact that there is a proof of decidability?
Edward van de Meent (Jan 14 2026 at 11:23):
No. That wouldn't work, because docs#Classical.propDecidable is a theorem
Edward van de Meent (Jan 14 2026 at 11:26):
(because if what you're arguing is possible, we would be able to prove (for example) FLT by using choice to create the decidable instance, then calling by decide.)
Bbbbbbbbba (Jan 14 2026 at 11:29):
But it would be nice to have a tactic that tries to decide up to a limit, and fails otherwise
Bbbbbbbbba (Jan 14 2026 at 11:29):
Could be used for e.g. sanity check by trying to find small counterexamples
Edward van de Meent (Jan 14 2026 at 11:30):
What does 'tries to decide' mean? There is no general algorithm for 'trying to decide', afaik...
Aaron Liu (Jan 14 2026 at 11:31):
do you mean like plausible, which tries to find counterexamples
Aaron Liu (Jan 14 2026 at 11:31):
note that you can never prove something with plausible, you can only find counterexamples
Edward van de Meent (Jan 14 2026 at 11:33):
Let me emphasize that the Issue here is not with decide, it's with docs#Nat.find not reducing depending on what proof is passed
Mario Weitzer (Jan 14 2026 at 11:38):
Edward van de Meent said:
Let me emphasize that the Issue here is not with
decide, it's with docs#Nat.find not reducing depending on what proof is passed
I understand, but how can I tell from the outset if a certain function (like Nat.find in this case) will or won't reduce and how can I force it to reduce or implement it in such a way that I can be sure it will reduce?
Edward van de Meent (Jan 14 2026 at 11:45):
Usually, things which aren't noncomputable reduce. There are exceptions which usually involve explicit calls to WellFounded.fix or Acc.rec, which aren't noncomputable but still might not reduce.
Bbbbbbbbba (Jan 14 2026 at 11:52):
Aaron Liu said:
note that you can never prove something with
plausible, you can only find counterexamples
In OP's use case, they may be able to use the counterexample as an explicit existence proof so that decide goes through :thinking:
Shubakur Mitra (Jan 14 2026 at 12:10):
This can be decided using the method proposed in https://leanprover.zulipchat.com/#narrow/channel/113488-general/topic/Proofs.20by.20computation/near/556707383
Eval.lean
import Eval
def f_pos_nz' (f : Nat → Nat) (h : f ≠ 0) := Nat.find (Function.ne_iff.mp h)
example : f_pos_nz' (λ n => n) (Function.ne_iff.mpr ⟨1, Nat.one_ne_zero⟩) = 1 := by
change C _; simp only [f_pos_nz', evalC, evalD]
Last updated: Feb 28 2026 at 14:05 UTC