Syntax for pattern matching QIT (and maybe HIT) in OTT (and maybe HOTT) [blog/qit-syntax]
Syntax for pattern matching QIT (and maybe HIT) in OTT (and maybe HOTT) [blog/qit-syntax]
By OTT, we mean Loïc Pujet and Nicolas Tabareau’s Impredicative Observational Equality.
A type a : A b : A
------------------------
a ~_A b : Prop
cast A B : A ~_U B → A → B
Open Data Types
In OCaml (and a proposal of Haskell), there is a feature alowing you to extend a data type with new constructors. Which is called “Extensible Variant Types” in OCaml, and “Open Data Types” in Haskell. For some reasons, it is never supported by GHC.
In OCaml, the eliminator (pattern matching) of an extensible type should always have a default case for the future extended constructors. While in Haskell, the eliminator of an open data type is an open function, a function defined with pattern matching that can adding new clauses. For example
open data Expr = Add Expr Expr
open pp : Expr → String
pp (Add a b) = pp a ++ " + " ++ pp b
data Expr += Lit Int
pp (Lit x) = show x
Those features can work perfectly with Agda style pattern matching syntax.
A Syntax for QIT in Non Cubical TT
In CuTT (and XTT), A HIT should be considered as an inductive type with “boundary conditions”, and functions should eliminate such types with respect to the conditions. But in OTT, we do not have such thing.
Basically, this note works out a syntax for, 1. how to define a QIT 2. how to eliminate it.
Define a QIT
Suppose there are indexed inductive types Eq_A a b : Prop over all QIT A where a b : A.
(Note. For cubical indexed inductive types, we need to add a new constructor transpX. In OTT, we need the similar thing, but Eq is a irrelevent proposition family, we can omit that on it, because we do not need to compute a proposition)
The observational equality ~ computes to Eq when acting on QIT. i.e.
a ~_A b = Eq_A a b for QIT A.
Thus we can translate a QIT, for example, the interval as follow,
data I : Type where
i0 i1 : I
data EqI : I → I → Prop where
seg : i0 ~ i1
For indexed inductive types,
data Ty : Cx → Type where
...
_[_] : {G D : Cx} → Ty D → Sb G D → Ty G
data EqTy {G : Cx} : Ty G → Ty G → Prop where
...
id[] : A [ id ] ~ A
-- A ~_{Ty G} B = EqTy {G} A B
The sur-syntax is as usual,
data I : Type where
i0 i1 : I
seg : i0 ~ i1
data Ty : Type where
...
_[_] : {G D : Cx} → Ty D → Sb G D → Ty Γ
...
id[] : A [ id ] ~ A
Eliminate a QIT
We define an open indexed inductive type,
A : U i x : A ⊢ B : U j
----------------------------- Formation
'(x : A) → B : U (imax i j)
f : '(x : A) → B a : A
--------------------------- Elimination
f $ a : B [ a / x ]
Sometimes we omit $, just write f a.
The introduction and computation rules are generated by pattern matching functions. For example, when we have the following definition,
add : ℕ → ℕ → ℕ
add zero y = y
add (suc x) y = suc (x + y)
We actually defined,
add : 'ℕ → ℕ → ℕ -- an introduction rule, so add is a constructor
(add $ zero) = λ y → y -- a computation rule
(add $ (suc x)) = λ y → suc (add x y) -- a computation rule
The elaborator should insert ' and $ automatically.
Note. ($) is an open function.
We have another open function for '(x : A) → B
apd : (f : '(x : A) → B) (e : u ~_A v) → cast B[u] B[v] (ap B[_] e) (f u) ~ f v
apd is a trivial function and can be automatically generated, if A is just a non-quotient inductive type.
So we focus only on the case when A is a QIT.
Every time we pattern match a QIT, e.g.
f : 'I → Bool
f $ i0 = true
f $ i1 = true
Then apd is lacking a clause for f.
apd f (e : a ~_I b) = ...
we can split on e, because a ~_I b is a inductive type (Eq_I).
apd f (seg : i0 ~_I i1) = ... : f i0 ~_Bool f i1
f i0 = true = f i1, so the hole can be filled by refl
apd f seg = refl
I hope all of these can make sense in some way, and I will try to write a experimental implementation of it to see if it works. XD
Examples
===============
F : I -> Bool -> Bool
F i0 true = true
F i0 false = false
F i1 true = true
F i1 false = false
find elimination F : 'I -> Bool -> Bool
missing apd clause
apd {a} {b} F (e : a ~_I b) : F a ~ F b
===============
f : I -> I -> Bool
f i0 i0 = True
f i0 i1 = False
f i1 i0 = True
f i1 i1 = False
find elimination f : 'I -> I -> Bool
missing apd clause
apd {a} {b} f (e : a ~_I b) : f a ~ f b
find elimination f (x : I) : 'I -> Bool
missing apd clause
apd {a} {b} (f x) (e : a ~_I b) : f x a ~ f x b
x can be further eliminated in apd
apd {a} {b} (f i0) e = ...
apd {a} {b} (f i1) e = ...
no need to proof congruence for apd
in apd POV, f and F are constructors in pattern matching sense
we don’t need to prove congruence if the target of elimination is a prop
Intuition:
'(x : A) -> Bis an open indexed inductive type, indexed byAandx.B,each functor defined with pattern matching onAis a constructor to this type.a ~_A bis an indexed inductive type overA,each quotient path is a new constructor to this type.apdis an open function eliminating'(x : A) -> Banda ~_A b, for quotient inductive typeA.when'(x : A) -> Bis extended, the apd function should also be extended with new clauses,which corresponds to the congruence.