Zulip Chat Archive
Stream: lean4
Topic: Structural recursion unfold theorems
Sky Wilshaw (Nov 24 2023 at 18:22):
I'm trying to generate an equational lemma for a function defined by structural recursion, but I can't work out how to prove the required equalities. #mwe:
inductive Foo
| zero : Foo
| succ : Foo → Foo
def replace (f : Foo → Option Foo) (t : Foo) : Foo :=
match f t with
| some u => u
| none =>
match t with
| .zero => .zero
| .succ t => .succ (replace f t)
#print replace
/-
def replace : (Foo → Option Foo) → Foo → Foo :=
fun f t =>
Foo.brecOn t fun t f_1 =>
match f t with
| some u => u
| none =>
(match (motive := (t : Foo) → Foo.below t → Foo) t with
| Foo.zero => fun x => Foo.zero
| Foo.succ t => fun x => Foo.succ x.fst.fst)
f_1
-/
theorem replace_eq (f : Foo → Option Foo) (t : Foo) :
replace f t = (match f t with
| some u => u
| none =>
match t with
| .zero => .zero
| .succ t => .succ (replace f t)) := by
unfold replace -- failed to generate unfold theorem for 'replace'
Sky Wilshaw (Nov 24 2023 at 18:24):
Even copy-pasting the #print-ed definition doesn't check as defeq to replace f t:
theorem replace_eq (f : Foo → Option Foo) (t : Foo) :
replace f t = (Foo.brecOn t fun t f_1 =>
match f t with
| some u => u
| none =>
(match (motive := (t : Foo) → Foo.below t → Foo) t with
| Foo.zero => fun x => Foo.zero
| Foo.succ t => fun x => Foo.succ x.fst.fst)
f_1) := by
rfl
/- tactic 'rfl' failed, equality lhs
replace f t
is not definitionally equal to rhs
Foo.brecOn t fun t f_1 =>
match f t with
| some u => u
| none =>
(match (motive := (t : Foo) → Foo.below t → Foo) t with
| Foo.zero => fun x => Foo.zero
| Foo.succ t => fun x => Foo.succ x.fst.fst)
f_1
-/
James Gallicchio (Nov 24 2023 at 19:05):
this seems like a bug in the equation generator?
Kyle Miller (Nov 24 2023 at 19:36):
I'm not sure exactly what unfold theorems are and how the relate to equation theorems, but simp can handle this.
theorem replace_eq (f : Foo → Option Foo) (t : Foo) :
replace f t = (match f t with
| some u => u
| none =>
match t with
| .zero => .zero
| .succ t => .succ (replace f t)) := by
cases t <;> simp [replace]
This cases t is important because the definition of replace proceeds by cases on t, so it's not unreasonable for it to decide to generate equation lemmas specialized for the different cases.
Kyle Miller (Nov 24 2023 at 19:38):
("failed to generate unfold theorem for 'replace'" does seem like a potential bug)
Sky Wilshaw (Nov 24 2023 at 19:45):
That fix also works in my real case, thanks!
Sky Wilshaw (Nov 24 2023 at 19:46):
I'd assumed that the equational lemmas would only be specialised to cases when the entire function was defined by pattern-matching - in this case, the match expression is internal so I assumed it wouldn't.
James Gallicchio (Nov 24 2023 at 19:57):
Even when it's defined by pattern matching you can unfold the definition to a match. See:
inductive Foo
| zero : Foo
| succ : Foo → Foo
def nonrecCasing (f : Foo → Option Foo) (t : Foo) : Foo :=
match f t with
| some u => u
| none =>
match t with
| .zero => .zero
| .succ t' => t'
example : nonrecCasing f t = .zero := by
unfold nonrecCasing
sorry
def recursive (t : Foo) : Foo :=
match t with
| .zero => .zero
| .succ t => .succ (recursive t)
example : recursive t = .zero := by
unfold recursive
sorry
def recCasing (f : Foo → Option Foo) (t : Foo) : Foo :=
match f t with
| some u => u
| none =>
match t with
| .zero => .zero
| .succ t' => recCasing f t'
example : recCasing f t = .zero := by
unfold recCasing
sorry
def recCasing2 (f : Foo → Option Foo) (t1 t2 : Foo) : Foo :=
match f t1 with
| some u => u
| none =>
match t2 with
| .zero => .zero
| .succ t' => recCasing f t'
example : recCasing2 f t1 t2 = .zero := by
unfold recCasing2
sorry
Let me look for related issues on the lean4 repo
Sky Wilshaw (Nov 24 2023 at 19:59):
Those unfolds don't work for me: failed to generate unfold theorem for 'recCasing'
James Gallicchio (Nov 24 2023 at 19:59):
Yep, that's the bug. It should work fine.
James Gallicchio (Nov 24 2023 at 19:59):
(recCasing is your original replace function)
James Gallicchio (Nov 24 2023 at 20:05):
and swapping the order of the matches also proceeds through the equation generator fine:
def recCasing' (f : Foo → Option Foo) (t : Foo) : Foo :=
match t with
| .zero =>
match f t with
| some u => u
| none => .zero
| .succ t' =>
match f t with
| some u => u
| none => recCasing' f t'
example : recCasing' f t = .zero := by
unfold recCasing'
sorry
--note this is the same function as before
example : recCasing = recCasing' := by
apply funext; intro f; apply funext; intro t
induction t <;> simp [recCasing, recCasing']
split <;> simp [*]
James Gallicchio (Nov 24 2023 at 20:31):
https://github.com/leanprover/lean4/issues/2962
Sky Wilshaw (Nov 24 2023 at 20:32):
Thanks for submitting the issue!
Last updated: May 02 2025 at 03:31 UTC