Table of Contents
Introduction
If F is injective and the type checker knows that F i = F j it could conclude i = j to solve further constraints.
For examples if F is a name of an inductive family or a constructor, it is injective. If F is defined then it is not injective in general, but in some important cases it is. For instance, define Vectors by recursion.
data Nat where
zero : Nat
suc : Nat -> Nat
data Empty where
data Prod (A B : Set) : Set where
pair : A -> B -> Prod A B
Vec : (A : Set) -> Nat -> Set
Vec A zero = Empty
Vec A (succ n) = Prod A (Vec A n)
Vec is an injective function, which can be shown by induction on Nat. I guess in this case injectivity could be statically checked. It would be nice to be able to specify that a function is injective (via a pragma), and this way get more support from the type checker.
Potential problems
Consider the following set of equations:
Vec A ?0 = Vec A ?1 ?1 = succ zero
Given the injectivity trick, we could solve these equations by first instantiating ?0 := ?1 and then ?1 := succ zero. Now, suppose we tried to solve the equations is the reverse order. We'd end up with the constraint Vec A ?0 = Prod A Empty which we can't solve with just the injectivity trick. There are two options:
- Never solve the constraints. For instance, by requiring that one side is neutral when applying injectivity.
- Always solve the constraints. This would be a bit trickier. Faced with the constraint
Vec A ?0 = Prod A Emptywe would have to figure out that this could only happen in thesucccase and instantiate?0 := succ ?2.
Bigger Example
Here is a bigger example, comparing two implementations of a type, one by induction, one by recursion.
module RecVsData where
-- empty type
data Empty : Set where
-- natural numbers
data Nat : Set where
Zero : Nat
Succ : Nat -> Nat
-- vectors
data Vec (A : Set) : Nat -> Set where
[] : Vec A Zero
_::_ : forall {n} -> A -> Vec A n -> Vec A (Succ n)
infixr 20 _::_
map : {A B : Set} -> (A -> B) -> forall {n} -> Vec A n -> Vec B n
map f [] = []
map f (x :: xs) = f x :: map f xs
-- Sized types, variables: i
Size = Nat
-- First coding of kinds, via an inductive datatype
module KindData where
data Kind : {_ : Size} -> Set where
mkKind : forall {i n} -> Vec (Kind {i}) n -> Kind {Succ i}
wkKind : forall { i } -> Kind { i } -> Kind { Succ i }
wkKind { Succ i } (mkKind ks) = mkKind (map wkKind ks)
-- Second coding of kinda, via recursion
module KindRec where
data MkKind (K : Set) : Set where
mkKind : forall { n } -> Vec K n -> MkKind K
Kind : {_ : Size} -> Set
Kind { Zero } = Empty
Kind { Succ i } = MkKind (Kind {i})
wkKind : forall { i } -> Kind { i } -> Kind { Succ i }
wkKind { Zero } ()
wkKind { Succ i } (mkKind ks) = mkKind (map wkKind ks)
-- THREE UNSOLVED MVARS HERE
-- RESOLVED IF INSTEAD: mkKind (map (wkKind { i }) ks)
-- A RECORD INSTEAD Of MkKind DOES NOT SOLVE THE PROBLEM
{- Manual type-checking
LHS ks => Vec (Kind { i }) ?1
Check mkKind (map wkKind ks) <= Kind { Succ (Succ i) }
= MkKind (Kind { Succ i })
Check map wkKind ks <= Vec (Kind { Succ i }) ?2
Infer map => forall {A B n} -> (A -> B) -> Vec A n -> Vec B n
Check wkKind <= A -> B
Infer wkKind ?3 => Kind { ?3 } -> Kind { Succ ?3 }
Infer map wkKind => Vec (Kind { ?3 }) ?4 -> Vec (Kind { Succ ?3 }) ?4
Check ks <= Vec (Kind { ?3 }) ?4
Solve ?3 = i Probably this setting is not performed, since Kind def.by.rec.
?4 = ?1
Infer map wkKind ks => Vec (Kind { Succ ?3 }) ?1
Solve ?2 = ?1
If one would rely on the *injectivity* of Kind, then
the setting ?3 = i could be made!!
-}