Do we really want special case casts? (was: [Agda] Proof involving associativity of ++ of vectors)

Samuel Gélineau gelisam at gmail.com
Sat Oct 17 21:19:25 CEST 2009


Wait a minute... why do we need special case casts? Couldn't we define
dependent versions of subst and cong once and for all? Of course we
can.


dependent-subst : {a : Set}{b : a → Set}
                → (P : ∀ x → b x → Set)
                → {x₁ : a}{y₁ : b x₁}
                → {x₂ : a}{y₂ : b x₂}
                → x₁ ≅ x₂
                → y₁ ≅ y₂
                → P x₁ y₁
                → P x₂ y₂
dependent-subst P refl refl p = p

dependent-cong : {a : Set}{b : a → Set}
               → {c : ∀ x → b x → Set}
               → (f : ∀ x y → c x y)
               → {x₁ : a}{y₁ : b x₁}
               → {x₂ : a}{y₂ : b x₂}
               → x₁ ≅ x₂
               → y₁ ≅ y₂
               → f x₁ y₁
               ≅ f x₂ y₂
dependent-cong f refl refl = refl


Unfortunately, these versions have a major disadvantage compared to
special case casts: they require an extra proof that (x₁ ≅ x₂). With
special case casts, this equality is inferred from refl.


List = Vec ℕ

list-subst : (P : ∀ x → List x → Set)
           → {x₁ : ℕ}{y₁ : List x₁}
           → {x₂ : ℕ}{y₂ : List x₂}
           → y₁ ≅ y₂
           → P x₁ y₁
           → P x₂ y₂
list-subst P refl p = p

list-cong : {c : ∀ x → List x → Set}
          → (f : ∀ x y → c x y)
          → {x₁ : ℕ}{y₁ : List x₁}
          → {x₂ : ℕ}{y₂ : List x₂}
          → y₁ ≅ y₂
          → f x₁ y₁
          ≅ f x₂ y₂
list-cong f refl = refl


Here is a shorter example illustrating the difference.


cong-len : ∀ {n m}
         → {xs : List n}
         → {ys : List m}
         → xs ≅ ys
         → n ≅ m
cong-len refl = refl

cong-arg : ∀ {n m}
         → (C : ℕ → Set)
         → {xs : C n}
         → {ys : C m}
         → xs ≅ ys
         → n ≅ m
cong-arg C refl = refl -- error
-- n != m of type ℕ
-- when checking that the pattern refl has type xs ≅ ys


refl implies (C n = C m), but that doesn't imply (n = m) because C
could be a function, which could easily return the same value given
distinct n and m. Vec, however, and by extension also List, is a type
constructor constant, and as such it always returns distinct types
given distinct argument. But whenever we wish to write a proof
involving the fact that some name is a type constant, we must write a
special case proof. I think it would be much better if, somehow, type
constructor constants were distinguished from arbitrary functions like
C. The key distinction, I think, is that Vec and List are neutral
terms, whereas C could by either normal or neutral.

Feature request: there should be a way to quantify over neutral terms only!

– Samuel


More information about the Agda mailing list