Zulip Chat Archive
Stream: lean4
Topic: Cannot show termination of non-recursive function
David Hamelin (Jan 10 2023 at 21:02):
Hello,
I am trying to prove that in the following code, test_continuation
terminates:
inductive Continuation : Type where
| done (a : Nat)
| step (f: Unit → Continuation)
open Continuation
instance : Inhabited Continuation where
default := done 0
def to_string : Continuation → String
| done a => s!"Done: {a}"
| step _ => s!"Not done"
partial def test_continuation : Nat → Continuation
| 2 => step (fun () => test_continuation 2)
| x => done x
#eval to_string (test_continuation 2)
I know that test_continuation
terminates (eval
returns immediately), because there is no recursive call : it returns a function wrapped in a Continuation
type.
However, when I remove the partial
keyword, Lean 4 shows the following error:
fail to show termination for
test_continuation
with errors
argument #1 was not used for structural recursion
failed to eliminate recursive application
test_continuation 2
structural recursion cannot be used
failed to prove termination, use `termination_by` to specify a well-founded relation
I tried to use decreasing_by
as shown in "Theorem Proving in Lean 4", but it generates a goal I cannot prove:
def test_continuation : Nat → Continuation
| 2 => step (fun () => test_continuation 2)
| x => done x
decreasing_by
simp_wf
-- Goal is 2 < a✝, with no hypothesis
sorry
Could this be proved ?
Thanks in advance
Sky Wilshaw (Jan 10 2023 at 21:06):
Lean's termination checker isn't really about termination of the executed program, it's termination of the definition. Because test_continuation 2
references test_continuation 2
(even though it's not executed immediately), the termination check fails.
Sky Wilshaw (Jan 10 2023 at 21:07):
In lean's kernel, there's no concept of "executing" a program, there's just type theory. To build recursive functions at a low level, we use recursors generated from inductive data types, which require this kind of "decreasing" check.
Kyle Miller (Jan 10 2023 at 21:07):
My understanding is similar to what Sky is referring to. If you start reducing the expression rather than evaluating it, you'd get step (fun () => step (fun () => step (fun () => step (fun () => ...))))
, and so on. So in that sense it fails to terminate.
Kyle Miller (Jan 10 2023 at 21:16):
But also, if you could prove termination, you'd get a contradiction:
inductive Continuation : Type where
| done (a : Nat)
| step (f: Unit → Continuation)
open Continuation
theorem uhoh (x : Continuation) (hx : x = step (fun () => x)) : False := by
induction x with
| done _ => cases hx
| step f ih =>
simp at hx
exact ih () (congrFun hx ())
axiom test_continuation : Nat → Continuation
axiom test_continuation.eq_1 : test_continuation 2 = step (fun () => test_continuation 2)
axiom test_continuation.eq_2 (x : Nat) (h : x ≠ 2) : test_continuation x = done x
example : False := uhoh (test_continuation 2) test_continuation.eq_1
David Hamelin (Jan 10 2023 at 22:04):
I see, thank you very much
I should read more about Lean's kernel to not get into another pitfall
Sebastian Ullrich (Jan 10 2023 at 22:19):
This would be a fine corecursive function, but Lean does not support that
Sky Wilshaw (Jan 10 2023 at 22:21):
David Hamelin said:
I should read more about Lean's kernel to not get into another pitfall
Feel free to keep asking questions about it too!
Last updated: Dec 20 2023 at 11:08 UTC