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: Dec 20 2023 at 11:08 UTC