Zulip Chat Archive

Stream: lean4

Topic: Dependent Pattern Matching


Reed Mullanix (Jan 13 2021 at 02:20):

I'm messing around with everyone's favorite toy example, length indexed vectors, and I can't figure out how to get pattern matching to do what I would expect.

inductive Vec (A : Type) : Nat  Type where
| vnil : Vec A 0
| vcons : {n : Nat}  A  Vec A n  Vec A (Nat.succ n)


def traverse_ {n : Nat} {A : Type} (v : Vec A n) (f : A  IO Unit) : IO Unit :=
  match v with
  | Vec.vnil => return ()
  | (Vec.vcons a v') => f a *> traverse v' f

This fails with the following message:

 test.lean     9   3 error           type mismatch
   Vec.vnil
 has type
   Vec A 0
 but is expected to have type
   Vec A n (lsp)

I would expect that matching on vnil would force us to unify n with 0. I also attempted to match on both n and v simultaneously, but that didn't seem to do much of anything.

Kyle Miller (Jan 13 2021 at 02:23):

Matching on both seems to work:

inductive Vec (A : Type) : Nat  Type where
| vnil : Vec A 0
| vcons : {n : Nat}  A  Vec A n  Vec A (Nat.succ n)


def traverse {n : Nat} {A : Type} (v : Vec A n) (f : A  IO Unit) : IO Unit :=
  match n, v with
  | 0, Vec.vnil => return ()
  | n+1, (Vec.vcons a v') => f a *> traverse v' f

Reed Mullanix (Jan 13 2021 at 02:28):

Weird! Yours works, but when I add parens around (n, v) in the match it breaks

Kyle Miller (Jan 13 2021 at 02:30):

I'm guessing it's because (n, v) is creating a term of Prod Nat (Vec A n), which is then being matched, and the product has no dependence between the two components.

Reed Mullanix (Jan 13 2021 at 02:30):

Ah, that would make sense. Thanks for the help :)

Kyle Miller (Jan 13 2021 at 02:31):

This typechecks, but it fails the termination check:

def traverse {A : Type} (v : Vec A n) (f : A  IO Unit) : IO Unit :=
  match Sigma.mk n v with
  | 0, Vec.vnil => return ()
  | _, Vec.vcons a v' => f a *> traverse v' f

Liang-Ting Chen (Apr 20 2021 at 08:15):

Hi, I am also playing with the typical example Vec, trying to define a type-safe head function as follows:

inductive Vec (α : Type) : Nat  Type where
  | nil  :               Vec α 0
  | cons : α  Vec α n  Vec α (1 + n)

namespace Vec
  def head (xs : Vec α (1 + n)) : α :=
    match xs with
    | cons x xs => x

But Lean4 complains

dependent elimination failed, stuck at auxiliary equation
Nat.add 1 n = 0

which I don't see how to tell Lean that nil is an absurd case. The Lean3 manual has exactly the same example, but that impossible case can just be ignored.

Sebastian Ullrich (Apr 20 2021 at 08:20):

Nat.add is defined by recursion on the second parameter. Are you sure it's not n + 1 in the Lean 3 example?

Daniel Fabian (Apr 20 2021 at 08:27):

using n.succ circumvents the problem, too, btw.

Liang-Ting Chen (Apr 20 2021 at 08:28):

Ooops!! Thanks for solving my problem so quickly!

Liang-Ting Chen (Apr 20 2021 at 08:34):

Sebastian Ullrich said:

Nat.add is defined by recursion on the second parameter. Are you sure it's not n + 1 in the Lean 3 example?

Yes, you're right. It is n + 1 in the Lean 3 manual. I wasn't copying the example directly, but typed manually. I am more used to defining these operations by recursion on the first parameter.


Last updated: Dec 20 2023 at 11:08 UTC