Zulip Chat Archive

Stream: lean4

Topic: pattern match for positive Nat


Asei Inoue (Feb 02 2025 at 08:48):

why this does not work?

abbrev PosNat := { x : Nat // x > 0}

@[match_pattern]
def PosNat.add (x y : PosNat) : PosNat :=
  x.val + y.val, by omega

instance : Add PosNat := PosNat.add

def PosNat.one : PosNat := 1, by decide

instance : OfNat PosNat 1 := PosNat.one

def seq (x : PosNat) :  :=
  match x with
  | 1 => 1
  /- invalid patterns, `n` is an explicit pattern variable, but it only occurs in positions that are inaccessible to pattern matching
  ⟨.(n.1).succ, ⋯⟩ -/
  | n + 1 => seq n

Kevin Buzzard (Feb 02 2025 at 11:26):

Because Posnat is not an inductive type with two constructors?

Aaron Liu (Feb 02 2025 at 11:28):

Because PosNat.add is modifying the proof term, and match expects a free variable there.

Aaron Liu (Feb 02 2025 at 11:31):

This works:

def seq (x : PosNat) :  :=
  match x with
  | 1 => 1
  | n + 2, _⟩ => seq n + 1, by omega

Kevin Buzzard (Feb 02 2025 at 11:32):

Match is cleverer than I thought!

Aaron Liu (Feb 02 2025 at 11:33):

Match is less clever than I thought.

François G. Dorais (Feb 02 2025 at 12:36):

Custom recursors allow using cases and induction without the fuss. It would be fun to have some kind of match helpers to get around this kind of issue.

Asei Inoue (Feb 02 2025 at 13:27):

@Aaron Liu

Thank you but this is not what I want.

def seq (x : PosNat) :  :=
  match x with
  | 1 => 1
  | n + 2, _⟩ => seq n + 1, by omega

In this approach, we need to construct a proof that n+1 is greater than 0 in order to create a PosNat term on the right-hand side of the match expression. Instead, I want to perform a matching like n + 1 => seq n.

Asei Inoue (Feb 02 2025 at 13:30):

Would it be possible to solve this by creating a custom macro or elaborator?

Aaron Liu (Feb 02 2025 at 13:31):

I would try that, but it seems complicated

Kevin Buzzard (Feb 02 2025 at 13:32):

With that kind of attitude we'll never prove Fermat's Last Theorem ;-)

Aaron Liu (Feb 02 2025 at 13:38):

what am I doing wrong

-- `error: invalid atom` on `1`
macro "match'" i:term "with" "|" "1" "=>" t1:term "|" n:ident "+" "1" "=>" ts:term : term =>
  `(match $i with | 1 => $t1 | n + 2, _⟩ => let $n := n + 1, n.zero_lt_succ⟩; $ts)

Edward van de Meent (Feb 02 2025 at 13:47):

there are rules as to what can and can't be a keyword in lean. in this particular instance, lean complains that "1" fails the requirements.

Asei Inoue (Mar 08 2025 at 06:58):

@Aaron Liu Thanks for your thoughts. I for one am glad to know how hard it is.

Robin Arnez (Mar 08 2025 at 07:28):

It's not the best solution but:

def PosNat := { x : Nat // x > 0 }

def PosNat.add (x y : PosNat) : PosNat :=
  x.1 + y.1, Nat.add_pos_left x.2 _⟩

instance : Add PosNat := PosNat.add

def PosNat.one : PosNat := 1, by decide

instance : OfNat PosNat 1 := PosNat.one

@[simp]
def PosNat.val_one : (1 : PosNat).val = 1 := rfl

@[simp]
def PosNat.val_add (a b : PosNat) : (a + b).val = a.val + b.val := rfl

@[cases_eliminator, macro_inline]
def PosNat.casesOn {motive : PosNat  Sort u}
    (one : motive 1) (succ :  k, motive (k + 1))
    (t : PosNat) : motive t :=
  match t with
  | 1, _⟩ => one
  | k + 2, _⟩ => succ k + 1, Nat.succ_pos _⟩

def seq (x : PosNat) : Int := by
  cases x with
  | one => exact 1
  | succ n => exact seq n
termination_by x.1

Asei Inoue (Mar 08 2025 at 07:42):

Thank you, Robin!

Weiyi Wang (Apr 02 2025 at 21:13):

lol I just struggled on the same thing with PNat and match a few days ago. I gave up and defined my thing using PNat.recOn instead


Last updated: May 02 2025 at 03:31 UTC