Zulip Chat Archive

Stream: new members

Topic: append and the related theorem for vector type


Shanghe Chen (Feb 13 2024 at 10:10):

Hi! I am learning TPIL and in the chapter Inductive Families I am trying to formalize a theorem related to append vector:

inductive Vector (α : Type u) : Nat  Type u where
| nil : Vector α 0
| cons : α  {n : Nat}  Vector α n  Vector α (n+1)

#check Vector.nil
#check Vector.cons 3 Vector.nil

#check Vector.recOn
-- Inductive_Families.Vector.recOn.{u_1, u} {α : Type u} {motive : (a : Nat) → Vector α a → Sort u_1} {a : Nat}
--   (t : Vector α a) (nil : motive 0 Vector.nil)
--   (cons : (a : α) → {n : Nat} → (a_1 : Vector α n) → motive n a_1 → motive (n + 1) (Vector.cons a a_1)) : motive a t

def length {α : Type u} (x : Vector α n) : Nat := n

-- TODO cannot do this now
--      How to prove or state Vector Nat (1+0) = Vector Nat 1?
-- def append {α : Type u} (x : Vector α n) (y : Vector α m) : Vector α (n+m) :=
-- Vector.recOn (motive := fun (a : Nat) (b : Vector α a) => Vector α (a+m)) x
-- (y)
-- sorry

Shanghe Chen (Feb 13 2024 at 10:11):

How to define the append fun for vector plz? It seems that I stucked at showing something like proving Vector Nat (1+0) = Vector Nat 1

Bolton Bailey (Feb 13 2024 at 10:51):

You might be looking for docs#cast

Bolton Bailey (Feb 13 2024 at 10:53):

(Casting things can become very tricky, which I would say is why dependent inductive types like this are generally avoided in mathlib)

Shanghe Chen (Feb 13 2024 at 10:55):

OK maybe I just stick with the Eq example in TPIL and leave alone the vector type

Shanghe Chen (Feb 13 2024 at 10:57):

So in fact Vector Nat (1+0), Vector Nat 1 they are not the same type? I suspect they are not?

Shanghe Chen (Feb 13 2024 at 12:34):

Now I get the def with the following code:

namespace InductiveFamilies

inductive Vector (α : Type u) : Nat  Type u where
| nil : Vector α 0
| cons (a: α) {n : Nat} (b: Vector α n) : Vector α (n+1)

#check Vector.nil
#check Vector.cons 3 Vector.nil

#check Vector.recOn
-- Inductive_Families.Vector.recOn.{u_1, u} {α : Type u} {motive : (a : Nat) → Vector α a → Sort u_1} {a : Nat}
--   (t : Vector α a) (nil : motive 0 Vector.nil)
--   (cons : (a : α) → {n : Nat} → (a_1 : Vector α n) → motive n a_1 → motive (n + 1) (Vector.cons a a_1)) : motive a t

def length {α : Type u} (x : Vector α n) : Nat := n

theorem vector_eq (h : n = m) : Vector α n = Vector α m :=
congrArg (Vector α) h

def append {α : Type u} (x : Vector α n) (y : Vector α m) : Vector α (n+m) :=
Vector.recOn (motive := fun (a : Nat) (b : Vector α a) => Vector α (a+m)) x
(by simp ; exact y)
(by
  intro a' n' x' xy'
  let xy := (Vector.cons a' xy')
  let h: Vector α (n' + m + 1) = Vector α (n' + 1 + m) := by simp [Nat.add_assoc, Nat.add_comm, vector_eq]
  let xy' := cast h xy
  exact xy'
  )

def append₁ {α : Type u} (x : Vector α n) (y : Vector α m) : Vector α (n+m) :=
match x with
| nil => sorry
| cons h t => sorry

end InductiveFamilies

except the error: code generator does not support recursor 'InductiveFamilies.Vector.recOn' yet, consider using 'match ... with' and/or structural recursion and invalid pattern, constructor or constant marked with '[match_pattern]' expected . Why I cannot run recOn on it, and why it's also failed with pattern match?

Robert Maxton (Feb 14 2024 at 11:00):

Honestly, that seems like a shame. At least in this case, it's not too bad. The syntax is a little weird at first, though

Shanghe Chen (Feb 14 2024 at 11:04):

If I added noncomputable keyword in the first def, it works but #eval fails. And for the pattern match I get it in Dependent pattern matching in TPIL now :tada:

Robert Maxton (Feb 14 2024 at 11:06):

The trick is to remember that "proofs" in Lean are just producing a term of a given type anyway, so you can use the := by syntax to produce terms of other types (types that are not in Prop) as well. So you can get started with something like:

  def append {α : Type u} {n m : } (v : Vector α n) (w : Vector α m) : Vector α (n + m) :=
    match v with
    | .nil => by convert w; simp
    | @Vector.cons _ hd k tl => by ...

.nil is easily done with convert, which in this case successfully realizes that the types Vector α m and Vector α (m+0) would be the same if m = m + 0, which you can then prove with simp. .cons is a bit harder; if you just use convert directly, convert will see that you have a term Vector α (k + 1 + m) and a term Vector α (k + m + 1), and will blindly try and equate atoms, producing the goals 1 = m and m = 1 instead of k + 1 + m = k + m + 1; you'll have to work around that. (I can also just give you the answer -- or at least an answer -- but since you're doing the exercises anyway I thought I'd give you the option at least :p)

Robert Maxton (Feb 14 2024 at 11:08):

...aaalso somehow I missed your last few posts ^.^;
oops!

Shanghe Chen (Feb 14 2024 at 11:12):

Yeah. Now I get the def of append in pattern match form with the following snippet:

inductive Vector (α : Type u) : Nat  Type u where
| nil : Vector α 0
| cons (a: α) {n : Nat} (b: Vector α n) : Vector α (n+1)
deriving Repr

#check Vector.nil
#check Vector.cons 3 Vector.nil

#check Vector.recOn
-- Inductive_Families.Vector.recOn.{u_1, u} {α : Type u} {motive : (a : Nat) → Vector α a → Sort u_1} {a : Nat}
--   (t : Vector α a) (nil : motive 0 Vector.nil)
--   (cons : (a : α) → {n : Nat} → (a_1 : Vector α n) → motive n a_1 → motive (n + 1) (Vector.cons a a_1)) : motive a t

def length {α : Type u} (x : Vector α n) : Nat := n

theorem vector_eq (h : n = m) : Vector α n = Vector α m :=
congrArg (Vector α) h

-- the noncomputable keyword is hint from
-- https://leanprover-community.github.io/archive/stream/270676-lean4/topic/code.20generator.20does.20not.20support.20recursor.html
noncomputable def append {α : Type u} (x : Vector α n) (y : Vector α m) : Vector α (n+m) :=
Vector.recOn (motive := fun (a : Nat) (b : Vector α a) => Vector α (a+m)) x
(by simp ; exact y)
(by
  intro a' n' x' xy'
  let xy := (Vector.cons a' xy')
  let h: Vector α (n' + m + 1) = Vector α (n' + 1 + m) := by simp [Nat.add_assoc, Nat.add_comm, vector_eq]
  let xy' := cast h xy
  exact xy'
  )

#check append (Vector.cons 3 Vector.nil) (Vector.cons 4 Vector.nil)
-- cannot eval, still problematic, but the following match version does work
-- TODO why is the recOn version problematic in eval?
-- #eval append (Vector.cons 3 Vector.nil) (Vector.cons 4 Vector.nil)

def append₁ {α : Type u} (x : Vector α n) (y : Vector α m) : Vector α (n+m) :=
match n, x with
| 0, _ => by simp; exact y
-- it must match n, x in the same time, and the first must be n' + 1 for giving implicit
-- argument to the second
| n'+1, Vector.cons a t =>
  by
    let xy := Vector.cons a (append₁ t y)
    let h: Vector α (n' + m + 1) = Vector α (n' + 1 + m) := by simp [Nat.add_assoc, Nat.add_comm, vector_eq]
    let xy' := cast h xy
    exact xy'

#check append₁ (Vector.cons 3 Vector.nil) (Vector.cons 4 Vector.nil)
-- TODO it eval to ⊢ Vector Nat (0 + 1 + (0 + 1))
-- how to make it to Vector Nat 2?
#eval append₁ (Vector.cons 3 Vector.nil) (Vector.cons 4 Vector.nil)

Shanghe Chen (Feb 14 2024 at 11:14):

It just contains a minor flaws that #eval append (Vector.cons 3 Vector.nil) (Vector.cons 4 Vector.nil) is of type Vector Nat (0 + 1 + (0 + 1)) but not Vector Nat 2

Robert Maxton (Feb 14 2024 at 11:16):

Nice!

Robert Maxton (Feb 14 2024 at 11:19):

Coriver Chen said:

It just contains a minor flasw that #eval append (Vector.cons 3 Vector.nil) (Vector.cons 4 Vector.nil) is of type Vector Nat (0 + 1 + (0 + 1)) but not Vector Nat 2

Hmmm... You could probably work around that y doing something like

def append₁ {α : Type u} (x : Vector α n) (y : Vector α m) {r : Nat} {h_r: r = n + m} : Vector α r :=...

but I don't know what that'd do to the usability of the actual function...

Shanghe Chen (Feb 14 2024 at 11:33):

Yeah it’s just some kind of practice illuminating the strength of the type system I think.


Last updated: May 02 2025 at 03:31 UTC