Zulip Chat Archive

Stream: lean4

Topic: confused about equality of type indices


Jesse Slater (Feb 12 2025 at 04:36):

I am working with heterogeneous lists and I am confused about when an HList parametrized by a List l can be coerced to be parametrized by l' = l. I don't understand why cons_split_snd is a problem when cons_split_fst and cons_split_snd' are fine. Is there a way I can get cons_split_snd to work without the let?

inductive HList {α : Type} (β : α  Type) : List α  Type
  | nil : HList β []
  | cons : β a  HList β as  HList β (a::as)

def HList.split (bs : HList β (las ++ ras)) : HList β las × HList β ras :=
  match las, bs with
  | [], rbs => (nil, rbs)
  | _ :: _, cons b bs => bs.split.map (cons b ·) id

theorem HList.cons_split_fst
  {α : Type} {a : α} {β : α  Type} {b : β a} {las ras : List α} {bs : HList β (las ++ ras)}
  : (cons b bs).split.fst = cons b bs.split.fst := by -- works
  rfl

theorem HList.cons_split_snd
  {α : Type} {a : α} {β : α  Type} {b : β a} {las ras : List α} {bs : HList β (las ++ ras)}
  : (cons b bs).split.snd = bs.split.snd := by -- application type mismatch
  rfl

theorem HList.cons_split_snd'
  {α : Type} {a : α} {β : α  Type} {b : β a} {las ras : List α} {bs : HList β (las ++ ras)}
  : let bs' : HList β ((a :: las) ++ ras) := cons b bs
    bs'.split.snd = bs.split.snd := by -- works
    rfl

Jesse Slater (Feb 12 2025 at 04:45):

Somewhat related follow up: I want to prove HList append is associative. My understanding is that since (a ++ b ++ c) is not definitionally equal to (a ++ (b ++ c)) for lists, HList.append_assoc requires a cast, which is not ideal. Is there any way to avoid the cast?

inductive HList {α : Type} (β : α  Type) : List α  Type
  | nil : HList β []
  | cons : β a  HList β as  HList β (a::as)
infixr:67 " ::ₕ " => HList.cons

def HList.append : HList β las  HList β ras  HList β (las ++ ras)
  | nil,        rbs => rbs
  | lb ::ₕ lbs, rbs => lb ::ₕ (lbs.append rbs)

instance HList.instHAppend : HAppend (HList β las) (HList β ras) (HList β (las ++ ras)) where
  hAppend := HList.append

theorem HList.append_assoc
  {as₁ as₂ as₃ : List α} (hl₁ : HList β as₁) (hl₂ : HList β as₂) (hl₃ : HList β as₃)
  : hl₁ ++ hl₂ ++ hl₃ = hl₁ ++ (hl₂++ hl₃) := by sorry -- type mismatch

def HList.cast {as as' : List α} (eq : as = as') (hl : HList β as) : HList β as' := by
  subst eq
  exact hl

theorem HList.cons_cast
  {a : α} {b : β a} {as as' : List α} {eq : as = as'} {bs : HList β as}
  : b ::ₕ bs.cast eq = (b ::ₕ bs).cast ((List.cons_inj_right a).mpr eq) := by
  subst eq
  rfl

theorem HList.cons_append
  {a : α} {b : β a} {as₁ as₂ : List α} {hl₁ : HList β as₁} {hl₂ : HList β as₂}
  : b ::ₕ hl₁ ++ hl₂ = b ::ₕ (hl₁ ++ hl₂) := by
  rfl

theorem HList.append_assoc'
  {as₁ as₂ as₃ : List α} (hl₁ : HList β as₁) (hl₂ : HList β as₂) (hl₃ : HList β as₃)
  : hl₁ ++ hl₂ ++ hl₃ = (hl₁ ++ (hl₂++ hl₃)).cast (List.append_assoc as₁ as₂ as₃).symm := by -- works
  induction hl₁
  case nil => rfl
  case cons a as b bs ih =>
    rw [cons_append, cons_append, ih]
    exact cons_cast

James Gallicchio (Feb 12 2025 at 05:21):

My impression is that the standard way to avoid cast is to introduce a specialization of cast for types that frequently need cast, as you do here. HList.cast should be much less problematic than cast. Your associativity lemma is already as clean as it can be!

James Gallicchio (Feb 12 2025 at 05:26):

As for your first question, I think the reason cons_split_fst works is because the RHS helps the LHS infer the las and ras arguments for split. We can explicitly pass those the las arg and cons_split_snd works:

theorem HList.cons_split_snd
  {α : Type} {a : α} {β : α  Type} {b : β a} {las ras : List α} {bs : HList β (las ++ ras)}
  : ((cons b bs).split (las := a::las)).snd = bs.split.snd := by
  rfl

James Gallicchio (Feb 12 2025 at 05:29):

Leaving las and ras implicit in HList.split is probably a footgun. For example if lean is asked to unify [a,b] = ?las ++ ?ras, what do you expect the behavior to be? There are multiple reasonable ways Lean could unify it. Instead, make at least one of las and ras explicit, so that the caller must make their expectation clear.

Jesse Slater (Feb 12 2025 at 21:02):

Thank you!


Last updated: May 02 2025 at 03:31 UTC