Zulip Chat Archive

Stream: lean4

Topic: let rec and refine


Alex Sanchez-Stern (Oct 24 2025 at 18:16):

Are there plans to allow using let rec with refine? Like:

def foo: Nat -> Nat := by {
   refine let rec f (n : Nat): Nat := ?_; f
}

Ideally this should produce a goal with n : Nat and f: Nat -> Nat in the context and Nat as the goal, though not sure if this interacts with the termination checker in a weird way. Right now it just gives an error on the ?_ saying "don't know how to synthesize placeholder".

I'm essentially trying to use this to produce a computable recursive function in tactic mode, because of the limitations of functions generated using induction. Writing a top level recursive definition doesn't work because I'm trying to build some machinery for automatically synthesizing Equivs between certain types.

Robin Arnez (Oct 24 2025 at 23:55):

Well, let rec more or less defines a top-level recursive definition though

Alex Sanchez-Stern (Oct 28 2025 at 18:01):

Sure, but it can be generated using tactics in a way that top-level recursive definitions can't. If necessary, I'll go up a level and use more general metaprogramming constructs, but this seems like a reasonable thing to support at the tactic level.

Wrenna Robson (Oct 30 2025 at 11:45):

My feeling is I don't know what you're doing exactly but you almost certainly shouldn't do it like this.

Alex Sanchez-Stern (Oct 30 2025 at 17:44):

You're saying, it shouldn't be possible to prove two types Equiv just using tactics? Or that it only shouldn't be possible when the types are inductive? For non-recursive types like structures it works just fine.

Aaron Liu (Oct 30 2025 at 20:01):

You shouldn't use tactics to construct data

Yves Jäckle (Nov 02 2025 at 09:25):

@Alex Sanchez-Stern
You can get a sort of computable induction with this workaround:

universe u

@[elab_as_elim]
def Nat.compRec {motive : Nat  Sort u}
  (zero : motive Nat.zero) (succ : (n : Nat)  motive n  motive n.succ)
  (t : Nat) : motive t :=
  match t with
  | 0 => zero
  | .succ n => succ n (Nat.compRec zero succ n)


def factorial : Nat  Nat := fun n => by {
  induction n using Nat.compRec with
  | zero => exact 1
  | succ n sofar => exact sofar*(n+1)
}

#eval factorial 2
#eval factorial 3
#eval factorial 4

Robin Arnez (Nov 02 2025 at 09:33):

You shouldn't do that though...

Robin Arnez (Nov 02 2025 at 09:34):

There's much better support for functions written with recursion.

Alex Sanchez-Stern (Nov 03 2025 at 19:17):

I mean, I wouldn't try to commit something with tactic-written functions to mathlib, but there are a lot of different applications of Lean where being able to prove types equivalent purely using bounded automation is useful. I care a lot about being able to use the tooling in a way that fully respects the curry-howard correspondence and doesn't have things you can only write in "proof style" or "program style"

Robin Arnez (Nov 03 2025 at 20:30):

You can also create global declarations using tactics (including recursive ones), if that's part of the concern. For that you can see docs#Lean.Elab.addPreDefinitions

Alex Sanchez-Stern (Nov 03 2025 at 20:41):

Ah, that does seem useful, thanks!


Last updated: Dec 20 2025 at 21:32 UTC