Zulip Chat Archive

Stream: general

Topic: Trouble with cases and rintro / rcases


Sorawee Porncharoenwase (Nov 06 2020 at 22:12):

Consider:

import tactic.basic

inductive val : Type
| nat (n : ) : val
| list (vs : list val) : val

inductive ok : val  Prop
| nat {n : } (h : n = 1) : ok (val.nat n)
| list_nil : ok (val.list [])
| list_cons {v vs} (h : ok v) (h' : ok (val.list vs)) : ok (val.list (v :: vs))

lemma test {v : val} {vs : list val} : ok (val.list (v :: vs))  ok v :=
begin
  intro h,
  cases h with a b,
  sorry,
end

I would expect the two hypotheses introduced by cases to be named a and b. Yet, they are named h_h and h_h' .

The same occurs when I change cases h with a b, to rcases h with ⟨a, b⟩,.

Lastly, consider:

import tactic.basic

inductive val : Type
| nat (n : ) : val
| list (vs : list val) : val

mutual inductive ok, ok_aux
with ok : val  Prop
| nat {n : } (h : n = 1) : ok (val.nat n)
| list {xs} (h : ok_aux xs) : ok (val.list xs)
with ok_aux : list val  Prop
| nil : ok_aux []
| cons {v vs} (h : ok v) (h' : ok_aux vs) : ok_aux (v :: vs)

lemma test {v : val} {vs : list val} : ok (val.list (v :: vs))  ok v :=
begin
  rintro x⟩,
end

At the end, it says "goals accomplished", but at the same time it says

tactic failed, result contains meta-variables
state:
no goals

Are these bugs or do I misunderstand anything?

Kevin Buzzard (Nov 06 2020 at 22:15):

I'm not at lean right now, but the last error is usually caused by a buggy tactic. Try recover to work around it.

Jannis Limperg (Nov 06 2020 at 22:24):

The first error is essentially an implementation detail of cases. In the with cause, you give names for all constructor arguments in order, so your a and b are interpreted as the names given to the arguments n and h of ok.nat. However, cases then realises that this constructor is impossible, so a and b never show up in the goal. Try this instead: cases h with _ _ _ _ a b.

Wrt the second issue, I would advise you to stay away from mutual inductive types (and ideally also nested inductives). They are shoddily implemented and basically untested, so you can expect a lot of random errors.

Sorawee Porncharoenwase (Nov 06 2020 at 22:35):

Thanks, cases h with _ _ _ _ a b works perfectly!

Eric Wieser (Nov 06 2020 at 23:45):

I think you can also use cases h and then case ok.list_cons : _ _ a b { sorry }

Simon Hudon (Nov 07 2020 at 04:27):

And you can write pretty_cases to see which position in the list of names names which variable.

Sorawee Porncharoenwase (Nov 07 2020 at 05:34):

@Simon Hudon pretty_cases doesn't seem to work on the above example. It generates

case [anonymous]
  { admit },

which is a syntax error.

I guess a related question is, is there a way to use case for case splitting arises from split or things like that? I currently use comment and indentation to informally structure the proof. E.g., currently I write

repeat { split },
  -- case A
  tactic_a,

  -- case B
  tactic_b,

  -- case C
  tactic_c,

But it would be nice to have an actual support from the language. E.g., being able to write:

repeat { split },
case [1] {
  tactic_a,
},
case [2] {
  tactic_b,
},
case [3] {
  tactic_c,
},

instead.

Simon Hudon (Nov 07 2020 at 05:38):

That's because pretty_cases, like case itself, relies on goal tags. induction and cases add tags to goals corresponding to the constructors. If you call any tactics afterwards (with a few exceptions), those tags will be erased.

Simon Hudon (Nov 07 2020 at 05:38):

In order to make your use case possible, you'd need split to tag the generated goals.

Simon Hudon (Nov 07 2020 at 05:40):

What benefit to do see in using case [1] { ... } instead of just { ... },?

Sorawee Porncharoenwase (Nov 07 2020 at 05:42):

I didn't know that that works! Time to refactor my code :)

Simon Hudon (Nov 07 2020 at 05:42):

:+1:

Simon Hudon (Nov 07 2020 at 05:43):

You might like refine_struct btw. It tags goals with the field names of a structure

Bryan Gin-ge Chen (Nov 07 2020 at 05:55):

@Simon Hudon somewhat related: do you understand #4779?

Simon Hudon (Nov 07 2020 at 05:58):

Have you tried using field f1 instead of case f1?

Simon Hudon (Nov 07 2020 at 05:59):

I did not consider mixing the two. The refine_struct machinery clashes with case but I don't think it needs to be the case.

Simon Hudon (Nov 07 2020 at 06:02):

You can see here the encoding of tags: https://github.com/leanprover-community/mathlib/blob/master/src/tactic/interactive.lean#L311

Simon Hudon (Nov 07 2020 at 06:04):

In comparison, cases and inductive use _case as a prefix. refine_struct also uses shorter tags. In case, you have to give the fully qualified name of the constructor and I wanted to avoid it.

Simon Hudon (Nov 07 2020 at 06:06):

We could use the same convention for refine_struct and cases / inductive and use case for both, changing case to allow the omission of qualifiers in constructor and field names

Bryan Gin-ge Chen (Nov 07 2020 at 06:12):

Ah, I see. Yes, field f1 does work. This was not at all clear to me from tactic#refine_struct and the tactic state.

Simon Hudon (Nov 07 2020 at 06:14):

I didn't think the proof state printing case foo would be a big deal but looking back, I realize that wasn't my best idea

Simon Hudon (Nov 07 2020 at 06:17):

Merging cases and refine_struct might be pretty hairy because of what case does to rename variables.

Jannis Limperg (Nov 07 2020 at 10:27):

Simon Hudon said:

In comparison, cases and inductive use _case as a prefix. refine_struct also uses shorter tags. In case, you have to give the fully qualified name of the constructor and I wanted to avoid it.

FYI, I changed case at some point so that you can now give shorter names as well. Essentially, case now looks for an unambiguous suffix, so in Sorawee's example, case list_cons would work.

Chris B (Nov 07 2020 at 17:07):

Small nitpick about rcases while it's being discussed; the doc string showing the syntax uses something called trans_rel that doesn't seem to exist.

Mario Carneiro (Nov 07 2020 at 17:18):

It's talking about docs#relation.trans_gen

Mario Carneiro (Nov 07 2020 at 17:23):

the suggested match syntax is also wrong for trans_gen, it should have 2 args in the first case and 4 in the second. But now I'm worried if I put that there that it will look confusing unless I also provide the definition of the inductive

Mario Carneiro (Nov 07 2020 at 17:23):

the point of the quote is just to say you can match on more complex inductive predicates

Chris B (Nov 07 2020 at 17:24):

Did the name get changed without the comment getting updated or do mathlib people know trans_rel |-> relation.trans_gen?

Chris B (Nov 07 2020 at 17:25):

I'm worried if I put that there that it will look confusing unless I also provide the definition of the inductive

I think if people can grep it or do like #print trans_gen and get something back it's fine right? (or use the doc search thing)

Mario Carneiro (Nov 07 2020 at 17:26):

Chris B said:

Did the name get changed without the comment getting updated or do mathlib people know trans_rel |-> relation.trans_gen?

No, I just recalled the name imperfectly from memory

Chris B (Nov 07 2020 at 18:05):

While I'm splitting hairs, I think rcases h e pat performs... is missing a colon between h and e.


Last updated: Dec 20 2023 at 11:08 UTC