Zulip Chat Archive

Stream: Is there code for X?

Topic: Dependent vector (tuple) notation


Niklas Halonen (Jul 20 2025 at 12:41):

Hi! I recently wanted to show the following equivalence using the vector notation for Fin d → α i.e. ![a, b, ...]:

import Mathlib.Tactic

def finTwoPiEquiv {α β : Type u} : ((i : Fin 2)  match i with | 0 => α | 1 => β)  α × β where
  toFun := fun f => f 0, f 1
  invFun := fun a, b => ![a, b]

However the above didn't type check because the vector notation doesn't support heterogeneous types, even though the underlying Fin code does support it. Do we have a notation for defining these dependent tuples and their types (my requirement is that it works without × and uses Fin d → ... instead)?

I'm sure it must exist but can't find it anywhere. In the meantime I just copied VecNotation.lean and made it dependent myself introducing a !(a, b, ...) notation:

import Mathlib.Tactic

/-- `!()` is the dependent vector with no entries. -/
def vecEmpty {α : Fin 0  Type*} (i : Fin 0) : α i := i.elim0

/-- `vecCons h t` prepends an entry `h` to a dependent vector `t`.

The inverse functions are `vecHead` and `vecTail`.
The notation `!(a, b, ...)` expands to `vecCons a (vecCons b ...)`.
-/
def vecCons {n : } {α : Fin n.succ  Type*} (h : α 0) (t : (i : Fin n)  α i.succ) (i : Fin n.succ) : α i :=
  Fin.cons h t i

syntax (name := dvecNotation) "!(" term,* ")" : term

macro_rules
  | `(!($term:term, $terms:term,*)) => `(vecCons $term !($terms,*))
  | `(!($term:term)) => `(vecCons $term !())
  | `(!()) => `(vecEmpty)

@[app_unexpander vecCons]
def vecConsUnexpander : Lean.PrettyPrinter.Unexpander
  | `($_ $term !($term2, $terms,*)) => `(!($term, $term2, $terms,*))
  | `($_ $term !($term2)) => `(!($term, $term2))
  | `($_ $term !()) => `(!($term))
  | _ => throw ()

@[app_unexpander vecEmpty]
def vecEmptyUnexpander : Lean.PrettyPrinter.Unexpander
  | `($_:ident) => `(!())
  | _ => throw ()

variable {n : } {α : Fin (n + 1)  Type*} (v : (i : Fin n.succ)  α i) (x : α 0)

/-- `vecHead v` gives the first entry of the vector `v` -/
def vecHead : α 0 :=
  v 0

/-- `vecTail v` gives a vector consisting of all entries of `v` except the first -/
def vecTail (i : Fin n) : α i.succ :=
  v (i.succ)

#check !(1) -- not a complete type
#check !(1, 2, 3) -- not a complete type
#check (!((1 : ), "a", [2]) : (i : Fin 3)  ![, String, List ] i)

section Val

-- @[simp]
-- theorem head_fin_const (a : α) : (vecHead fun _ : Fin (n + 1) => a) = a :=
  -- rfl

variable {n : } {α : Fin (n + 1)  Type*} (v : (i : Fin n.succ)  α i) (x : α 0)
  (u : (i : Fin n)  α i.succ)

@[simp]
theorem cons_val_zero : vecCons x u 0 = x :=
  rfl

-- theorem cons_val_zero' (h : 0 < m.succ) (x : α) (u : Fin m → α) : vecCons x u ⟨0, h⟩ = x :=
  -- rfl

@[simp]
theorem cons_val_succ (i : Fin n) : vecCons x u i.succ = u i := by
  simp [vecCons]

-- @[simp]
-- theorem cons_val_succ' {i : ℕ} (h : i.succ < m.succ) (x : α) (u : Fin m → α) :
--     vecCons x u ⟨i.succ, h⟩ = u ⟨i, Nat.lt_of_succ_lt_succ h⟩ := by
--   simp only [vecCons, Fin.cons, Fin.cases_succ']

@[simp]
theorem head_cons : vecHead (vecCons x u) = x :=
  rfl

@[simp]
theorem tail_cons : vecTail (vecCons x u) = u := by
  ext
  simp [vecTail]

-- @[simp]
-- theorem empty_val' {n' : Type*} (j : n') : (fun i => (![] : Fin 0 → n' → α) i j) = ![] :=
  -- empty_eq _

@[simp]
theorem cons_head_tail : vecCons (vecHead v) (vecTail v) = v :=
  Fin.cons_self_tail _

-- [Skipped some stuff]

def vecAppend {α : Fin m  Type u} {β : Fin n  Type u} (u : (i : Fin m)  α i)
  (v : (i : Fin n)  β i) : (i : Fin (m + n))  Fin.append α β i :=
  Fin.addCases
    (fun i => Fin.append_left α β i  u i)
    (fun i => Fin.append_right α β i  v i)

lemma Fin.eq_natAdd_sub (i : Fin (m + n)) (h : m  i) : i = Fin.natAdd m (i : ) - m, by omega := by
  simp [h]

theorem vecAppend_eq_ite {α : Fin m  Type u} {β : Fin n  Type u}
  (u : (i : Fin m)  α i) (v : (i : Fin n)  β i) :
    vecAppend u v = fun i : Fin (m + n) =>
      if h : (i : ) < m then Fin.append_left α β i, h  u i, h
      else
        -- Need to chain these two equalities
        -- Fin.append α β i  =  Fin.append α β (Fin.natAdd m ⟨↑i - m, ⋯⟩)  =  β ⟨↑i - m, ⋯⟩
        (congrArg _ (Fin.eq_natAdd_sub i (not_lt.mp h))).trans
        (Fin.append_right α β (i : ) - m, by omega) 
          (v (i : ) - m, by omega) := by
  ext i
  simp [vecAppend, Fin.addCases]
  -- rw [vecAppend, Fin.addCases, Function.comp_apply, Fin.addCases]
  congr with hi
  simp [Fin.subNat]
  -- Don't know how to proceed
  -- rfl
  sorry

end Val



def finTwoPiEquiv {α β : Type u} : ((i : Fin 2)  match i with | 0 => α | 1 => β)  α × β where
  toFun := fun f => f 0, f 1
  invFun := fun a, b => !(a, b) -- Dependent VecNotation!
  -- invFun := Function.uncurry (!(., .)) also works
  left_inv := by
    intro f
    simp
    -- The goal is pretty printed as ⊢ !(f 0, f 1) = f
    ext i
    fin_cases i <;> rfl
  right_inv := by
    tauto

Now my original equivalence can be proved as above.

This still has a bit of a problem with instance synthesis, for example to define the Repr instance for ((i : Fin n) → α i):

instance {h :  i, Repr (α i)} : Repr ((i : Fin n)  α i) where ...

it requires synthesis of Repr instances for arbitrarily (but finitely) many α is, but don't know if that exists in Lean or not.


Last updated: Dec 20 2025 at 21:32 UTC