Zulip Chat Archive
Stream: general
Topic: DecidableEq for nested inductive types
Jeremy Avigad (Mar 23 2024 at 17:36):
Consider this definition:
inductive Term where
| var (x : String)
| app (f : String) (args : List Term)
I just managed to provide an instance of DecidableEq Term
, but it wasn't easy. Is there a better way to do it?
In any case, I am impressed that Lean can handle such recursive definitions.
Kyle Miller (Mar 23 2024 at 19:09):
With a few helper functions for Decidable
, you can write it pretty much like a boolean function directly, which I think is about the bare minimum you'd expect if you had to write an equality function:
instance (p : Prop) : CoeOut (Decidable p) Bool := ⟨@decide p⟩
@[inline] def dtrue {p : Prop} (h : p := by simp) : Decidable p := isTrue h
@[inline] def dfalse {p : Prop} (h : ¬ p := by simp) : Decidable p := isFalse h
@[inline] def decide_by {p : Prop} (q : Prop) [Decidable q] (h : q ↔ p := by simp) : Decidable p :=
decidable_of_iff q h
inductive Term where
| var (x : String)
| app (f : String) (args : List Term)
namespace Term
mutual
protected def deq : (x : Term) → (y : Term) → Decidable (x = y)
| var x, var y => decide_by (x = y)
| var .., app .. => dfalse
| app .., var .. => dfalse
| app f₁ args₁, app f₂ args₂ => decide_by (f₁ == f₂ && Term.deq_list args₁ args₂)
protected def deq_list : (xs : List Term) → (ys : List Term) → Decidable (xs = ys)
| [], [] => dtrue
| [], _::_ => dfalse
| _::_, [] => dfalse
| x::xs, y::ys => decide_by (Term.deq x y && Term.deq_list xs ys)
end
instance : DecidableEq Term := Term.deq
Kyle Miller (Mar 23 2024 at 19:10):
Little warning about your version: if you do by simp; infer_instance
or use rw
, it's possible to get Decidable
terms that can't reduce (for example, it can cause the decide
tactic to fail). It's better to use decidable_of_iff
, which pushes the rewrite into the proofs. Here, I wrapped that up in decide_by
.
Kyle Miller (Mar 23 2024 at 19:19):
A neat thing that works out with my version is that the compiled code even respects the short-circuiting behavior of &&
, so it's as efficient as it looks (if you treat decide_by
as being this weird cast you have to insert.)
Kyle Miller (Mar 23 2024 at 19:26):
(Ideally we could just write deriving DecidableEq
, but that doesn't support nested inductives yet.)
Last updated: May 02 2025 at 03:31 UTC