Zulip Chat Archive
Stream: lean4
Topic: implementing ReflBEq or DecidableEq instance
Pavel Klimov (Sep 20 2025 at 13:04):
I have following proof
theorem test (a : List Nat): ((a.zip (a.map (fun x => x ^ 2))).all fun (x, y) => x ^ 2 = y) := by
induction a with
| nil => rfl
| cons x xs ih =>
simp [List.zip, List.map] at ih ⊢
trivial
I suppose it works because of BEq.rfl theorem, which states
BEq.rfl.{u_1} {α : Type u_1} [BEq α] [ReflBEq α] {a : α} : (a == a) = true
Why I think so? Well... More close to my actual case is following code
inductive MyType
| a
| b
deriving BEq
def myFunc : Nat → MyType
| 1 => .a
| _ => .b
theorem test1 (a : List Nat): ((a.zip (a.map (fun x => myFunc x))).all fun (x, y) => myFunc x == y) := by
induction a with
| nil => rfl
| cons x xs ih =>
simp [List.zip, List.map] at ih ⊢
trivial
You may ask "why not use deriving DecidableEq instead?". I'll answer a bit later. But you should see here this proof doesn't work anymore. So, while trying to prove it manually, I arrived at theorem "BEq.rfl", and found out that if I add instance ReflEq:
instance : ReflBEq MyType where
rfl := by
intro v
induction v
. rfl
. rfl
Then proof of theorem test1 will work once again. But here comes the twist. The type for which I want to implement ReflBEq actually something like:
inductive Node
| leaf
| node (children : List Node)
deriving BEq
And here, I can't use deriving DecidableEq, it says
default handlers have not been implemented yet, class: 'DecidableEq' types: [Node]
And induction tactic says
'induction' tactic does not support nested inductive types, the eliminator 'Node.rec' has multiple motives
So I tried tactic cases, and then very fist case has goal
⊢ (Node.leaf == Node.leaf) = true
And rfl doesn't work anymore. And I don't know how to prove it. I don't even know how to unfold ==. I tried rw [BEq.beq], and it did something, but then I don't know what to do next.
I also tried to use recursors:
instance : ReflBEq Node where
rfl := by
intro v
exact (Node.rec
(motive_1 := fun y => (y == y) = true)
(motive_2 := fun y => (List.all y (fun z => z == z)) = true)
(leaf := by rfl)
(node := fun children => by simp_all)
(nil := by simp)
(cons := fun head tail => by intro z; simp_all)
v
)
Lines nil, cons works, but leaf and node asking proofs of same cases as in cases tactic.
Then I tried to implement BEq instance myself, to be able to use its definition, but then I also stuck, but this time I was able to write implementation, but I'm unable to prove its termination:
def Node.size : Node → Nat
| .leaf => 0
| .node children => 1 + (children.map fun x => x.size).max?.getD 0
--mutual
def singleEq : Node → Node → Bool
| .leaf, .leaf => true
| .node children, .node children1 =>
children.length == children1.length
&& (children.zip children1).all fun (a, b) =>
have hm : a.size < (Node.node children).size := by
rw [Node.size];
induction children with
| nil => sorry
| cons x xs ih => sorry
singleEq a b
| _, _ => false
termination_by a => a.size
I'm new to formal proofs, so probably I'm doing something wrong, for example metrics of decreasing I pick is wrong, etc.
So... My question is following: How can I define ReflBEq, or, perhaps DecidableEq in case like that? What should I do?
Aaron Liu (Sep 20 2025 at 13:12):
try writing a BEq or DecidableEq yourself!
Aaron Liu (Sep 20 2025 at 13:12):
you can copy off of if you get stuck
Pavel Klimov (Sep 20 2025 at 13:13):
If you read carefully, you will see that I tried to implement them myself
Thanks for the link, will check it out.
Pavel Klimov (Sep 20 2025 at 14:11):
I was able to implement DecidableEq for my type. It has 7 constructors, so my question is: is it normal to have 49 cases? It's because TestTy.noConfusion each time has different specification. And, when I have
| case3 (num : Nat) (v : TestTy)
to have its proof using tactics?
Stefan Kusterer (Sep 20 2025 at 14:37):
Hi Pavel,
to your question
It has 7 constructors, so my question is: is it normal to have 49 cases?
I guess you need to check all combinations, which gives you n^2 cases for n constructors.
I'm not sure of it makes sense in your case, but maybe you can use multiple types instead of one and in combination with inheritance.
I had a similar issue with DeciableLT - my code can be found here.
Regards, Stefan
Pavel Klimov (Sep 20 2025 at 18:30):
Can I avoid using recursors? Is it really good to have ReflBEq? Because my proof is working with only DecidableEq, but as a practice I tried to define ReflBEq. This is what I got:
mutual
def Node.beq : Node → Node → Bool
| .leaf, .leaf => true
| .node children1, .node children2 => Node.listBeq children1 children2
| _, _ => false
def Node.listBeq : List Node → List Node → Bool
| .nil, .nil => true
| x1 :: xs1, x2 :: xs2 => Node.beq x1 x2 && Node.listBeq xs1 xs2
| _, _ => false
end
instance : BEq Node where
beq := Node.beq
instance : BEq (List Node) where
beq := Node.listBeq
instance : ReflBEq Node where
rfl := by
intro v
exact (Node.rec
(motive_1 := fun x => (Node.beq x x) = true)
(motive_2 := fun y => (Node.listBeq y y) = true)
(leaf := by rfl)
(node := fun y => by
induction y with
| nil => simp_all; trivial
| cons x xs ih => intro ih1; trivial
)
(nil := by trivial)
(cons := fun head tail => by intro z; simp_all!)
v
)
Pavel Klimov (Sep 20 2025 at 18:33):
Also, why it doesn't work if I write == instead of Node.beq and Node.listBeq?
Last updated: Dec 20 2025 at 21:32 UTC