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 rfl could close this? Never mind, I guess this does not strip off any complexity...

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