Zulip Chat Archive
Stream: general
Topic: Help understanding DecidableRel stuck metavariable problem
Chris Moline (Dec 04 2025 at 01:46):
Hi. When I have
def comp {foo} [inst : LT foo]
[DecidableRel (·<·)]
(a:Array foo) (b:Array foo) (idx:Nat) : Ordering :=
match a[idx]?, b[idx]? with
| some v, some w => if @LT.lt foo inst v w
then comp a b (idx+1)
else Ordering.gt
| some v, none => Ordering.gt
| none, some v => Ordering.lt
| none, none => Ordering.eq
instance {foo} [Ord foo] : Ord (Array foo) where
compare xs ys := comp xs ys 0
I get an error:
error: LeanSitter/Length.lean:43:24: typeclass instance problem is stuck, it is often due to metavariables
When I change '[DecidableRel (·<·)]' to ' [DecidableRel (fun a b : foo => @LT.lt foo inst a b)]' I get
rror: LeanSitter/Length.lean:42:4: fail to show termination for
LeanSitter.Length.comp
with errors
failed to infer structural recursion:
Not considering parameter foo of LeanSitter.Length.comp:
it is unchanged in the recursive calls
Not considering parameter inst of LeanSitter.Length.comp:
it is unchanged in the recursive calls
Not considering parameter #3 of LeanSitter.Length.comp:
it is unchanged in the recursive calls
Not considering parameter a of LeanSitter.Length.comp:
it is unchanged in the recursive calls
Not considering parameter b of LeanSitter.Length.comp:
it is unchanged in the recursive calls
Cannot use parameter idx:
failed to eliminate recursive application
comp a b (idx + 1)
failed to prove termination, possible solutions:
- Use `have`-expressions to prove the remaining goals
- Use `termination_by` to specify a different well-founded relation
- Use `decreasing_by` to specify your own tactic for discharging this kind of goal
idx : Nat
⊢ idx + 1 < idx
I don't understand what's going on. What do I need to do?
Aaron Liu (Dec 04 2025 at 02:00):
the termination prover couldn't automatically prove that your algorithm doesn't go into an infinite loop
Malvin Gattinger (Dec 04 2025 at 09:11):
Maybe like this?
def comp {foo} [inst : LT foo]
[DecidableRel (fun a b : foo => @LT.lt foo inst a b)]
(a:Array foo) (b:Array foo) (idx:Nat) : Ordering :=
match ha : a[idx]?, hb : b[idx]? with
| some v, some w => if @LT.lt foo inst v w
then comp a b (idx+1)
else Ordering.gt
| some _, none => Ordering.gt
| none, some _ => Ordering.lt
| none, none => Ordering.eq
termination_by
b.size - idx
decreasing_by
rcases Array.getElem_of_getElem? ha with ⟨_, _⟩
rcases Array.getElem_of_getElem? hb with ⟨_, _⟩
omega
More example how to use termination_by and decreasing_by can be found here in #tpil.
Robin Arnez (Dec 04 2025 at 17:49):
For your DecidableRel problem: use [DecidableLT foo] instead. For your termination problem: you can use something like termination_by a.size - idx (i.e the difference of size and index should decrease in recursive calls). With some other adjustments:
def comp [LT α] [DecidableLT α] (a b : Array α) (idx : Nat) : Ordering :=
match h : a[idx]?, b[idx]? with
| some v, some w =>
if v < w then comp a b (idx+1) else Ordering.gt
| some _, none => Ordering.gt
| none, some _ => Ordering.lt
| none, none => Ordering.eq
termination_by a.size - idx
decreasing_by grind
Although I will note that there already is an Ord instance for Arrays, docs#Array.instOrd
Chris Moline (Dec 04 2025 at 20:48):
Thank you very much for the help. I didn't know you could write 'a : a[idx]?' to bind the result of 'a[idx]?'. I also haven't covered much for tactics either so to learn about rcases and grind was good. I've updated my code to use the array Ord instance.
I will strive to do better in the hopes of one day being useful to the lean community.
Last updated: Dec 20 2025 at 21:32 UTC