Zulip Chat Archive

Stream: lean4

Topic: tactic for dependent elimination?


Assia Mahboubi (Mar 28 2024 at 15:07):

The following case analysis fails:

import Mathlib
set_option autoImplicit false

inductive test_fam : Bool  Type
where
 | Tr : test_fam true
 | Fa : test_fam false

lemma test_app_not_ok (f : Bool  Bool) (h : forall b, test_fam (f b)) (b : Bool) : b = b := by
  cases (h b)

complaining that dependent elimination failed, failed to solve equation f b = true. The same cases tactic however works as expected when h is changed for (h : : forall b, test_fam (b) (b : Bool)), as in:

here
 lemma test_app_ok (f : Bool  Bool) (h : forall b, test_fam b) (b : Bool) : b = b := by
  cases (h b)

What is the appropriate way of performing this case analysis?

Johan Commelin (Mar 28 2024 at 15:10):

@Assia Mahboubi Do you by chance intend cases f b?

Assia Mahboubi (Mar 28 2024 at 15:13):

No, I really want to perform a case analysis on h b : test_fam (f b). I am expecting Lean to open two identical subgoals, identical to the initial state in this toy, reduced example.

Johan Commelin (Mar 28 2024 at 15:16):

I can do

lemma test_app_not_ok (f : Bool  Bool) (h : forall b, test_fam (f b)) (b : Bool) : b = b := by
  match f b, h b with
  | true, .Tr => _
  | false, .Fa => _

Johan Commelin (Mar 28 2024 at 15:16):

But I have no idea how to do cases directly on h b

Assia Mahboubi (Mar 28 2024 at 15:18):

Indeed. Unfortunately, it is quite Inconvenient as such for the application I have in mind...

Assia Mahboubi (Mar 28 2024 at 15:19):

Andcase (h b) works in Rocq/Coq so I guess there must be a way in Lean too.

Johan Commelin (Mar 28 2024 at 15:26):

According to the docstring for cases it should be possible to use cases H : h b. But it doesn't seem to do what it promises.

Kyle Miller (Mar 28 2024 at 17:19):

It is inconvenient, but generally when you get "dependent elimination failed" you can use generalize on indices manually.

lemma test_app_not_ok (f : Bool  Bool) (h : forall b, test_fam (f b)) (b : Bool) : b = b := by
  have x := h b
  generalize hb : f b = b' at x
  cases x
  /-
  case Tr
  f: Bool → Bool
  h: (b : Bool) → test_fam (f b)
  b: Bool
  hb: f b = true
  ⊢ b = b

  case Fa
  f: Bool → Bool
  h: (b : Bool) → test_fam (f b)
  b: Bool
  hb: f b = false
  ⊢ b = b
  -/

There ought to be an extension to cases that lets you provide names to use for the equalities to generalize indices like this.

Johan Commelin (Mar 28 2024 at 17:42):

I think @Assia Mahboubi wants to explicitly avoid writing out f b, because in her use case this is some horribly long expression. (I'm not 100% this is the reason, so please correct me if I'm wrong, Assia.)

Assia Mahboubi (Mar 28 2024 at 18:07):

Thanks for the suggestion! Indeed, I do not want this, first because my real example has many indices, and also because this is a building block of some automation, which becomes much less interesting if you have to potentially input these expressions.

Assia Mahboubi (Mar 28 2024 at 18:08):

I would be curious to hear the authors of LeanSSR about the behavior of there variant of elimination tactic.

Assia Mahboubi (Mar 28 2024 at 18:09):

@Ilya Sergey @Vladimir Gladstein @George Pîrlea

Kyle Miller (Mar 28 2024 at 19:13):

(@Johan Commelin That's exactly why I think it's inconvenient and would want a tactic to do this — I almost mentioned it explicitly)

Kyle Miller (Mar 28 2024 at 19:15):

@Assia Mahboubi I'm not an author of LeanSSR, but I've been studying its code, and as far as I can tell the elimination tactic does cases without any additional index processing.

Kyle Miller (Mar 28 2024 at 19:17):

Mathlib 3 had fairly undocumented induction' and cases' tactics that I believe could handle this case. https://leanprover-community.github.io/mathlib_docs/tactic/induction.html

I don't believe they've been ported, and I also don't remember them giving you equalities for the indices.

Mario Carneiro (Mar 28 2024 at 20:34):

Note that this is also part of the operation of cases itself: it already generalizes indices to equalities, then tries to eliminate those equalities using injection and subst, and the error you see about dependent elimination occurs when these methods fail. So another alternative would be to modify cases or add an option so that it passes these hypotheses back in the subgoals instead of failing if it can't eliminate them all.

Mario Carneiro (Mar 28 2024 at 20:36):

A drawback is that there isn't really any place to put the hypothesis names, but you can always use rename_i

Assia Mahboubi (Mar 29 2024 at 09:02):

Let me change my example slightly so as to better illustrate what I would need:

lemma test_app_ok (f : Bool  Bool) (h : forall b, test_fam (f b)) (b : Bool) : f b = b := by
-- cases H : h b fails but the following works and is doing what I want
have hh := (@test_fam.rec (fun (bb : Bool) (h : test_fam bb)  bb = b))
apply (hh _  _ (h b))

Assia Mahboubi (Mar 29 2024 at 09:07):

The approach in Rocq/Coq amounts to providing some support for helping the proof assistant with guessing the expected value of the motive argument of the eliminator. In this examples Rocq/Coq's unification does not need any extra information to guess this value, but some syntax is available for providing user input, as the general case is a too difficult higher-order unification problem.

Mario Carneiro (Mar 29 2024 at 09:34):

if that motive is sufficient, then you can just use generalize : f b = bb without introducing the equality

Johan Commelin (Mar 29 2024 at 10:03):

But again, you have to write out f b.

Kyle Miller (Mar 29 2024 at 17:55):

@Assia Mahboubi For what it's worth, cases h b is successfully computing a motive here. It even handles generalizing indices itself (like what Mario pointed out). As I understand it, cases h b is doing something like the following until it gets stuck on the last line:

lemma test_app_not_ok (f : Bool  Bool) (h : forall b, test_fam (f b)) (b : Bool) : f b = b := by
  have := h b
  generalize hx : f b = x at this 
  cases this
  cases hx
  /-
  dependent elimination failed, failed to solve equation
    true = f b
  -/

(In actuality, things are set up so that x = f b is in the target when applying the eliminator, and then it does intro and tries to do dependent elimination.)

Kyle Miller (Mar 29 2024 at 18:10):

It would be neat being able to tell cases either to not do dependent elimination or to do it in a weak way.


Last updated: May 02 2025 at 03:31 UTC