Zulip Chat Archive

Stream: new members

Topic: how to name new hypothesis using cases h with in Lean 4


Brando Miranda (Mar 06 2024 at 20:18):

Obvious newbie question. I've search lean4 manual, TP in Lean, MIL and more but can't make it work. I have a disjunction in my hypothesis. I did:

    -- to do inv cancel let's break h_zero_abs_x into both cases 0 < x and 0 < -x and prove both cases
    #check Or
    cases h_zero_abs_x with -- TODO: how to name hypothesis with cases in lean4
      | Or.inl h_x_pos => sorry
      | Or.inr h_x_neg => sorry

but lean4 doesn't like it. What am I doing wrong? I'd like to name my new hypothesis corresponding to each input to the constructor myself.

Error

invalid alternative name 'Or.inl'

but I hovered over Or and it says it exists

Or (a b : Prop) : Prop
Or a b, or a  b, is the disjunction of propositions. There are two constructors for Or, called Or.inl : a  a  b and Or.inr : b  a  b, and you can use match or cases to destruct an Or assumption into the two cases.

import Init.Prelude

so I'm puzzled.

Thanks in advance!

Kyle Miller (Mar 06 2024 at 20:23):

With cases syntax, you need to use inl and inr rather than Or.inl and Or.inr

Kyle Miller (Mar 06 2024 at 20:24):

These names come from the names of the constructors themselves, and in particular, the names of the arguments to Or.casesOn

Or.casesOn {a b : Prop} {motive : a  b  Prop} (t : a  b) (inl :  (h : a), motive ) (inr :  (h : b), motive ) :
  motive t

Brando Miranda (Mar 06 2024 at 20:37):

Sorry if this is a dumb question. What confuses me is that cases seems to have very similar syntax to match statements in function defs. When I tried defining addition for MyNats it insisted (unless I did open MyNats) that I used MyNats. So I am still confused why Lean 4 knows what constructors to use without open while in this case seems to magically know it. e.g., Why isn't it Or.casesOn.inl then?

Kyle Miller (Mar 06 2024 at 20:39):

It's similar to match, but it's not match. It's confusing that it looks the same but it has different behavior, but the simple answer is that it's just a different tactic.

A longer answer might be that cases doesn't work using pattern matching. You're not allowed use match on a proof anyway.

Kyle Miller (Mar 06 2024 at 20:40):

It's seeing it's Or because you're passing it h_zero_abs_x and it can see the type, and since cases only gives you cases for each "minor premise" of Or.casesOn, there's no need to qualify the names.

Kyle Miller (Mar 06 2024 at 20:41):

Potentially, cases could be modified to allow writing Or.inl and Or.inr, or at least detect that you've done it and tell you what to write instead.

Kyle Miller (Mar 06 2024 at 20:43):

By the way, for MyNats, if you write .succ, if Lean can figure out that the expected type is a MyNat, it will read that as MyNat.succ. That saves needing to open MyNat if you're just trying to avoid writing MyNat.succ, at the small cost of prefixing the constructor with a dot.

Brando Miranda (Mar 06 2024 at 20:59):

Super helpful Kyle. Thank you. However, sadly I'm still struggling to tell lean to manipulate my goal.

Now that I did

    cases h_zero_abs_x with -- TODO: how to name hypothesis with cases in lean4
      | inl h_x_pos => by
        have h_x_ne_zero : x  0 := by exact ne_of_gt h_x_pos
      | nr h_x_neg => sorry -- have h_neg_x_ne_zero : -x ≠ 0 := ne_of_gt h_x_neg

I am trying to construct the proof inside each case. But it keeps giving me syntax error e.g.,

unexpected token 'by'; expected '?', '_', '{' or tactic

I feel it's complaint make sense, since I am not stating a goal (so perhaps it's angry at the having another by) but deconstructing the hypothesis. But then I don't understand how I'm suppose to construct the proof for each case if Lean 4 doesn't let me generate the tactic proofs as I'd normally would.

Ruben Van de Velde (Mar 06 2024 at 20:59):

Remove the by after =>

Brando Miranda (Mar 06 2024 at 23:16):

Ruben Van de Velde said:

Remove the by after =>

I had tried that. But, what was wrong is that my later case had a type constructor. It says nr instead of inr. However, it was hard to me to predict that would cause a bug in the current line I was actually trying to construct a proof on. Now I know cases needs both constructors to be done properly or it fails.

Brando Miranda (Mar 06 2024 at 23:20):

fyi, if I have a analogous bug with match (typo in second constructor) it doesn't make Lean4 completely unusable on my other cases. e.g.,

inductive MyUnderyNat : Type
| O : MyUnderyNat
| S : MyUnderyNat -> MyUnderyNat

def add : MyUnderyNat -> MyUnderyNat -> MyUnderyNat
| MyUnderyNat.O, n => n
| MyUnderyNat.Su m, n => MyUnderyNat.S (add m n)

ie putting my cursor on n doesn't confuse lean4.

Brando Miranda (Mar 06 2024 at 23:23):

Kyle Miller said:

By the way, for MyNats, if you write .succ, if Lean can figure out that the expected type is a MyNat, it will read that as MyNat.succ. That saves needing to open MyNat if you're just trying to avoid writing MyNat.succ, at the small cost of prefixing the constructor with a dot.

What did you have in mind for this? I tried a specific example by defining the add function without prefixing but Lean4 didn't like it:

inductive MyUnderyNat : Type
| O : MyUnderyNat
| Succ : MyUnderyNat -> MyUnderyNat

def add : MyUnderyNat -> MyUnderyNat -> MyUnderyNat
| MyUnderyNat.O, n => n
| MyUnderyNat.Succ m, n => MyUnderyNat.Succ (add m n)

def add' (m n : MyUnderyNat) : MyUnderyNat :=
  match m with
  | O => n
  | Succ m' => Succ (add' m' n)

error

basic_nats.lean:12:24
Messages (1)
basic_nats.lean:12:4
invalid pattern, constructor or constant marked with '[match_pattern]' expected

Scott Kovach (Mar 06 2024 at 23:44):

Kyle was talking about this:

inductive Foo | bar
example : Foo := bar  -- unknown identifier
example : Foo := .bar -- ok; expected type is Foo, so this is resolved to mean Foo.bar

Mark Fischer (Mar 07 2024 at 00:09):

This is an aside, but in most cases the following two should be the same.

... := by exact ne_of_gt h_x_pos
... := ne_of_gt h_x_pos

by is a keyword that brings you into tactic mode while exact is a tactic that accepts terms (so you can think of it as bringing you back into term-mode.) Most of the time instances of by exact are redundant.

Brando Miranda (Mar 07 2024 at 01:28):

Scott Kovach said:

Kyle was talking about this:

inductive Foo | bar
example : Foo := bar  -- unknown identifier
example : Foo := .bar -- ok; expected type is Foo, so this is resolved to mean Foo.bar

got it, thanks. The example that worked for me what

def add' (m n : MyUnderyNat) : MyUnderyNat :=
  match m with
  | .O => n
  | .Succ m' => .Succ (add' m' n)

I didn't know before this that inductive Foo | bar would define a inductive type with cons bar and that .bar made sense. Never seen that notation in any other programming language. Cool!

Brando Miranda (Mar 07 2024 at 01:35):

very related. When I do cases h_zer_lt_abs_x it introduces new hypothesis into my local context with a name with a unicode. I assume this is the reason whenever I try to refer to it I can't use it or rename it. How do I refer to a hypothesis that was automatically named? Or that might have a special unicode? e.g.,
My current tactic

    cases h_zero_lt_abs_x

leads to this hypothesis being introduced (as expected):

h: 0 < x

but even if I copy paste the symbol ✝ or pair of symbols or manually write "\dagger", I can't refer to that hypothesis. How do I refer to that hypothesis so I can actually make progress in my proof? (ideally rename it to h_x_pos).

Mark Fischer (Mar 07 2024 at 02:57):

With Nat, refl & step works for me.

opaque G : Prop
example (h: 0 < x) : G := by
    cases h with
    | refl => sorry
    | @step n t => sorry

Brando Miranda (Mar 07 2024 at 05:53):

I don't think that is what I was looking for (?). I'm not using the with statement in this second version of using the cases tactic.

Brando Miranda (Mar 07 2024 at 05:53):

Lean produces goals with funny names I can refer to no matter what I do. I can't rename them or use them. But I want them of course, hence, why I called cases :)

Johan Commelin (Mar 07 2024 at 06:38):

@Brando Miranda Does rename_i h_x_pos do what you want?

Mario Carneiro (Mar 07 2024 at 06:42):

Brando Miranda said:

I didn't know before this that inductive Foo | bar would define a inductive type with cons bar and that .bar made sense. Never seen that notation in any other programming language. Cool!

Note that the syntax there is a bit compressed and possibly misleading. The more usual way to write it would be

inductive Foo
  | bar

which is shorthand for

inductive Foo : Type where
  | bar : Foo

Mario Carneiro (Mar 07 2024 at 06:45):

Likewise .bar is shorthand for Foo.bar, getting the type Foo from the expected type. It has precedent in Swift

Paul Rowe (Mar 07 2024 at 21:50):

Brando Miranda said:

Lean produces goals with funny names I can refer to no matter what I do. I can't rename them or use them. But I want them of course, hence, why I called cases :)

If you just use cases h_zero_abs_x without the with then all the cases will appear in the infoview labeled e.g. case inl and case inr. You can then use the case tactic to focus on whichever goal you want: case inl. If you add an arguments after the case identifier, it will start naming the inaccessible hypotheses: case inl h_x_pos will give you what you're looking for.

Paul Rowe (Mar 07 2024 at 21:54):

By the way, older versions of Lean 3 used to automatically name such hypotheses according to some pre-defined convention. While that was convenient for not having to name everything, it makes proofs quite brittle to changes in Lean, Mathlib, etc.

Matt Diamond (Mar 08 2024 at 02:31):

could a moderator move @Samyak Tuladhar 's post to a new thread? thanks

Edit: never mind, moved it myself

Notification Bot (Mar 08 2024 at 02:34):

A message was moved from this topic to #new members > Code is not live compiling by Matt Diamond.


Last updated: May 02 2025 at 03:31 UTC