Zulip Chat Archive
Stream: metaprogramming / tactics
Topic: Determining whether a definition is recursive
Jannis Limperg (Oct 25 2023 at 12:53):
I'm trying to determine whether a name d refers to a recursive definition. My first idea was to check whether d occurs in its own value, but that doesn't work for well-founded and mutual recursion. E.g. the mutual block
mutual
  def f : Nat → Nat
    | 0 => 0
    | n + 1 => g n
  def g : Nat → Nat
    | 0 => 0
    | n + 1 => f n
end
compiles to f x = f._mutual ... x ... with f_mutual an application of WellFounded.fix and f nowhere in sight.
I think I can work around this for my actual application, but I'd still be curious whether there's a nice way to figure this out.
Damiano Testa (Oct 25 2023 at 12:55):
Does docs#Lean.isRec help?
Mario Carneiro (Oct 25 2023 at 12:56):
no, that tells you if a definition is an inductive recursor
Mario Carneiro (Oct 25 2023 at 12:57):
you could either look at the equation lemmas, or you could look at the compiled IR to find the self reference
Mario Carneiro (Oct 25 2023 at 12:57):
Although, f._mutual in the definition is also a clear indicator that the definition is recursive
Alex J. Best (Oct 25 2023 at 12:59):
You could also recursively search through definitions of constants occuring in the value (knowing that you can ignore declarations defined in some dependent module)
Mario Carneiro (Oct 25 2023 at 13:06):
@Alex J. Best that won't help, the kernel definition is in fact acyclic
Mario Carneiro (Oct 25 2023 at 13:06):
that's what the fix reduction is about
Alex J. Best (Oct 25 2023 at 13:06):
Oh right!
Mario Carneiro (Oct 25 2023 at 13:09):
I think you can use docs#Lean.Compiler.mkUnsafeRecName to get the name of the compiler's partial def corresponding to a recursive function, if it exists and/or is a mutualDefnDecl then I think you found a recursive function
Mario Carneiro (Oct 25 2023 at 13:15):
import Lean
mutual
  def f : Nat → Nat
    | 0 => 0
    | n + 1 => g n
  def g : Nat → Nat
    | 0 => 0
    | n + 1 => f n
end
mutual
  def f' : Nat → Nat
    | 0 => 0
    | n + 1 => g' n
  def g' : Nat → Nat
    | 0 => 0
    | n + 1 => g' n
end
open Lean
def isRecursive (env : Environment) (n : Name) : Bool := Id.run do
  env.contains (Lean.Compiler.mkUnsafeRecName n)
#eval show Elab.Command.CommandElabM _ from do
  println! isRecursive (← getEnv) ``f -- true
  println! isRecursive (← getEnv) ``g -- true
  println! isRecursive (← getEnv) ``Nat.add -- true
  println! isRecursive (← getEnv) ``Nat.succ -- false
  println! isRecursive (← getEnv) ``Nat -- false
  println! isRecursive (← getEnv) ``f' -- false
  println! isRecursive (← getEnv) ``g' -- true
Last updated: May 02 2025 at 03:31 UTC