Zulip Chat Archive

Stream: new members

Topic: Noncomputable local functions


Sébastien Boisgérault (Jan 02 2026 at 12:22):

Hi everyone,

Is it possible to declare a local (recursively defined) function as noncomputable?

My use case would be something like:

example (p :   Prop): ( n,  m  n, p m) ->  ns :   , StrictMono ns   i, p (ns i) := by
  intro h
  let rec ns (i : ) :  :=
    match i with
    | 0 => Classical.choose (h 0)
    | i + 1 => Classical.choose (h (ns i))
  admit -- TODO

If it's not possible (?) what would be your suggested refactoring/workaround?

Best regards,

Sébastien

Mirek Olšák (Jan 02 2026 at 12:30):

Do you need the outer function to be computable? If not, you can mark the full example as noncomputable.

Mirek Olšák (Jan 02 2026 at 12:34):

Hm, it works with noncomputable def but I see the problem. If you want to use the preferred keyword theorem, then local definitions fail on computability, while you cannot mark theorem as noncomputable. Looks like a bug to me...

Mirek Olšák (Jan 02 2026 at 12:43):

There is also this way of making a truly noncomputable theorem

noncomputable section in
theorem my_thm (p :   Prop) : ( n,  m  n, p m) ->  ns :   , StrictMono ns   i, p (ns i) := by
  intro h
  let rec ns (i : ) :  :=
    match i with
    | 0 => Classical.choose (h 0)
    | i + 1 => Classical.choose (h (ns i))
  admit -- TODO

Alfredo Moreira-Rosa (Jan 02 2026 at 13:21):

Theorems are noncomputable.

Mirek Olšák (Jan 02 2026 at 13:28):

But the auxiliary definitions inside aren't (which is the topic of this thread).

Robin Arnez (Jan 02 2026 at 13:30):

This is lean4#7878, right?

Aaron Liu (Jan 02 2026 at 13:56):

I would do let ns : Nat → Nat := Nat.rec (Classical.choose (h 0)) (fun n ih => Classical.choose (h ih))

Aaron Liu (Jan 02 2026 at 13:57):

then you can do stuff with the contents too

Sébastien Boisgérault (Jan 02 2026 at 14:05):

Mirek Olšák said:

But the auxiliary definitions inside aren't (which is the topic of this thread).

Yes, thanks a lot for the feedback. So theorems (or examples) are noncomputable, but you can still declare them as noncomputable and it's stronger somehow, since their auxiliary definitions become noncomputable. Ah, that's a bit tricky!

Sébastien Boisgérault (Jan 02 2026 at 14:12):

Aaron Liu said:

I would do let ns : Nat → Nat := Nat.rec (Classical.choose (h 0)) (fun n ih => Classical.choose (h ih))

Beginner question sorry, but ... why does it work? A special property of recursors? A more general principle (such as having a non-computable function returned by a function instead of declared?)?

Robin Arnez (Jan 02 2026 at 14:13):

That works because it does not declare a new declaration

Robin Arnez (Jan 02 2026 at 14:15):

Only let rec declares a declaration, regular lets have no problem

Sébastien Boisgérault (Jan 02 2026 at 14:15):

Robin Arnez said:

This is lean4#7878, right?

Yes, thanks for the link! :pray:

Mirek Olšák (Jan 02 2026 at 14:31):

And by the way, if you apply choice before defining your recursive function, you don't run into this issue.

theorem my_thm (p :   Prop) : ( n,  m  n, p m) ->  ns :   , StrictMono ns   i, p (ns i) := by
  intro h
  choose ns nsh using h
  let rec nsr (i : ) :  :=
    match i with
    | 0 => ns 0
    | i + 1 => ns (nsr i)
  let n := ns 5
  sorry -- TODO

To understand what is going on, you can

#print my_thm

You will see that Lean automatically defined my_thm.nsr which takes ns as an argument. So it is equivalent as if you write

def my_thm.nsr (ns :   ) ...

theorem my_thm ...

Here the definition of my_thm.nsr is computable because its input is a function. When you did it before, it was equivalent to

def my_thm.nsr (h :  n,  m ...) := ... choice ...

theorem my_thm ...

Lean didn't see prefix noncomputable before the first def, and threw an error. The solution with noncomputable section in is then equivalent to

noncomputable section

def my_thm.nsr (h :  n,  m ...) := ... choice ...

theorem my_thm ...

end

Mirek Olšák (Jan 02 2026 at 14:41):

In this case, I would recommend using the choose tactic. In my experience, it is much more convenient to use over Classical.choose. But I can easily imagine other noncomputable functions you could need in local definitions.


Last updated: Feb 28 2026 at 14:05 UTC