Zulip Chat Archive
Stream: mathlib4
Topic: Why Nat.find is computable, when Wellfounded.fix isn't?
Paradoxy (Oct 15 2025 at 22:45):
I cannot understand this piece of code
protected def findX : { n // p n ∧ ∀ m < n, ¬p m } :=
@WellFounded.fix _ (fun k => (∀ n < k, ¬p n) → { n // p n ∧ ∀ m < n, ¬p m }) lbp (wf_lbp H)
(fun m IH al =>
if pm : p m then ⟨m, pm, al⟩
else
have : ∀ n ≤ m, ¬p n := fun n h =>
Or.elim (Nat.lt_or_eq_of_le h) (al n) fun e => by rw [e]; exact pm
IH _ ⟨rfl, this⟩ fun n h => this n <| Nat.le_of_succ_le_succ h)
0 fun _ h => absurd h (Nat.not_lt_zero _)
From import Mathlib.Data.Nat.Find.
While WellFounded.fix is noncomputable, somehow findX is accepted as computable? Why?
Aaron Liu (Oct 15 2025 at 23:06):
If you import batteries then it becomes computable
Aaron Liu (Oct 15 2025 at 23:06):
probably there's a better way to write the findX without explicitly referencing Wellfounded.fix
Paradoxy (Oct 15 2025 at 23:11):
Aaron Liu said:
If you import batteries then it becomes computable
Sorry, I don't follow. Let me make my example even more mininal, please explain the behaviour we observe:
def e1 (p : Type* → Prop) (ep : ∃ a, p a) := ep.choose
def e2 (p : Type → Prop) (ep : ∃ a, p a) := ep.choose
noncomputable def e3 (p : Nat → Prop) (ep : ∃ a, p a) := ep.choose -- Why do I have to make it noncomputable?!
noncomputable def e4 (t : Type)(p : t → Prop) (ep : ∃ a, p a) := ep.choose -- Why do I have to make it noncomputable?!
noncomputable def e5 (t : Type*)(p : t → Prop) (ep : ∃ a, p a) := ep.choose -- Why do I have to make it noncomputable?!
Surely I shouldn't import batteries for this, no? I expect everything here to be noncomputable. yet e1 and e2 are computable just fine, despite the fact that I am literally using Classical.choose, a noncomputable function.
Aaron Liu (Oct 15 2025 at 23:17):
not sure if this is good
/-- Find the smallest `n` satisfying `p n`. Returns a subtype. -/
protected def findX : { n // p n ∧ ∀ m < n, ¬p m } :=
go H 0 fun _ h => absurd h (Nat.not_lt_zero _)
where
go (H : ∃ n, p n) (k : Nat) (hk : ∀ n < k, ¬p n) : { n // p n ∧ ∀ m < n, ¬p m } :=
if pk : p k then ⟨k, pk, hk⟩
else
have : ∀ n ≤ k, ¬p n := fun n h =>
Or.elim (Nat.lt_or_eq_of_le h) (hk n) fun e => by rw [e]; exact pk
go H (k + 1) fun n h => this n <| Nat.le_of_succ_le_succ h
termination_by (wf_lbp H).wrap k
decreasing_by exact ⟨rfl, this⟩
Aaron Liu (Oct 15 2025 at 23:18):
Paradoxy said:
Aaron Liu said:
If you import batteries then it becomes computable
Sorry, I don't follow. Let me make my example even more mininal, please explain the behaviour we observe:
def e1 (p : Type* → Prop) (ep : ∃ a, p a) := ep.choose def e2 (p : Type → Prop) (ep : ∃ a, p a) := ep.choose noncomputable def e3 (p : Nat → Prop) (ep : ∃ a, p a) := ep.choose -- Why do I have to make it noncomputable?! noncomputable def e4 (t : Type)(p : t → Prop) (ep : ∃ a, p a) := ep.choose -- Why do I have to make it noncomputable?! noncomputable def e5 (t : Type*)(p : t → Prop) (ep : ∃ a, p a) := ep.choose -- Why do I have to make it noncomputable?!Surely I shouldn't import batteries for this, no? I expect everything here to be noncomputable. yet e1 and e2 are computable just fine, despite the fact that I am literally using Classical.choose, a noncomputable function.
no batteries specifically does WellFounded.fix along with some other functions (Acc.ndrec, Acc.ndrecOn, WellFounded.fixF, WellFounded.fix) in Batteries/WF by writing @[csimp] lemmas for them
Paradoxy (Oct 15 2025 at 23:22):
Aaron Liu said:
not sure if this is good
/-- Find the smallest `n` satisfying `p n`. Returns a subtype. -/ protected def findX : { n // p n ∧ ∀ m < n, ¬p m } := go H 0 fun _ h => absurd h (Nat.not_lt_zero _) where go (H : ∃ n, p n) (k : Nat) (hk : ∀ n < k, ¬p n) : { n // p n ∧ ∀ m < n, ¬p m } := if pk : p k then ⟨k, pk, hk⟩ else have : ∀ n ≤ k, ¬p n := fun n h => Or.elim (Nat.lt_or_eq_of_le h) (hk n) fun e => by rw [e]; exact pk go H (k + 1) fun n h => this n <| Nat.le_of_succ_le_succ h termination_by (wf_lbp H).wrap k decreasing_by exact ⟨rfl, this⟩
Thanks, this is educational. But can you also tell me why exactly e1 and e2 are computable?
Aaron Liu (Oct 15 2025 at 23:22):
oh that's because types are erased at runtime
Aaron Liu (Oct 15 2025 at 23:22):
so there's no data there
Paradoxy (Oct 15 2025 at 23:29):
Aaron Liu said:
so there's no data there
Can you please tell me where can I learn more about this? To my untrained eyes, if e1 and e2 are computable, then so must be e4 and e5.
Aaron Liu (Oct 15 2025 at 23:31):
it's the same reason you don't have to mark your proofs as noncomputable
Aaron Liu (Oct 15 2025 at 23:31):
there's nothing to compute
Aaron Liu (Oct 15 2025 at 23:32):
but for e3 you compute a Nat and for e4 you compute a t
Paradoxy (Oct 15 2025 at 23:36):
Aaron Liu said:
there's nothing to compute
What are we computing in e4 and e5 exactly that makes things noncomputable, that we don't compute in e1 and e2, making them computable? Sorry if I sound too stupid
Aaron Liu (Oct 15 2025 at 23:37):
we are computing the term of type t
Aaron Liu (Oct 15 2025 at 23:37):
for example, I could specialize t := Nat
Paradoxy (Oct 15 2025 at 23:41):
Aaron Liu said:
for example, I could specialize
t := Nat
Thank you very much, I understand it now
Last updated: Dec 20 2025 at 21:32 UTC