I've been reading the HoTT book and thinking about how it could
influence the design of Typer.
I can't see how to integrate something like the univalence axiom because
it seems problematic from a programming-language point of view (at
least for now).
But the feature that I can see more or less how to provide in Typer is
higher-order inductive types (HIT). These are basically inductive types
annotated with additional constraints in the form of
equality constructors.
E.g.
type ListSet α
| nil
| cons α (ListSet α)
with nomulti: (x ∈ S) -> (S = cons x S)
| noorder: Permutation S₁ S₂ -> (S₁ = S₂)
The idea here is that rather than try to design some representation of
sets which is canonical, we use some convenient representation
(e.g. a list) and then state boldly the additional properties we need
for it to be "canonical".
And by magic, the ListSet is thus made canonical: `nomulti` and
`noorder` are now *new* constructors for the equality type `Eq`.
In order for those equality to make sense, the HIT system imposes that
whenever a piece of code wants to look at a ListSet, it has to
promise/prove that it will obey those additional properties (i.e. that
its return value will not depend on the ordering of elements in the
list or on the number of repetitions of a given element).
HITs can also have additional equality constraints between equality
proofs, i.e. meta-equality proofs, and meta-meta-equality, ...
[ homotopy type theory spends most of its time playing with equality
proofs ], but I'm not very interested in those for now.
I don't want to extend Typer's inductive types with such fanciness:
I already find those inductive types to be "too big" and would rather
decompose them into smaller elements (tags, sums, and products), but I'd
be interested in providing some support for it in Typer.
So, let's start with a particular instance of a HIT: the quotient
types/sets. Let's call `Quotient T R`, the type of elements of type
T where if two elements of x1:T and x2:T are in the relation
R (i.e. there's a proof P : R x1 x2) then we consider them equivalent:
Quotient : (τ : Type) → (τ → τ → Type) → Type;
Qin : τ → Quotient τ R;
Qeq : R x₁ x₂ → Qin{R} x₁ = Qin{R} x₂;
and then the eliminator needs to have a type along the lines of:
Qout : (f : τ → α) → (R x₁ x₂ → f x₁ = f x₂) ≡> (x : Quotient τ R) → α;
where `f` is the function which gets to look at the element `x` and
where the argument of type (R x₁ x₂ → f x₁ = f x₂) is the proof that
this function `f` will treat "equals as equals" (if given two
equivalent inputs, it will return equivalent outputs).
As always, I find it useful to try to represent it via the impredicative
encoding:
Quotient τ R = (α : Type) ≡> (f : τ → α) → (R x₁ x₂ → f x₁ = f x₂) ≡> → α;
Qin x = λα ≡> λ f → λ _ ≡> f x;
Qout x = x;
and interestingly, we see that the argument that proves that `f` is
well-behaved is not used here. Instead it's needed in `Qeq`: in order to
prove `R x₁ x₂ → Qin{R}x₁ = Qin{R}x₂` we can use that proof argument to
argue that the function `Qin{R}x₁` is extensionally equivalent to the
function `Qin{R}x₂`. So we need the functional extensionality axiom in
order to prove this and thus provide an impredicative encoding of
Quotient types.
In practice, such an encoding is rather problematic, as usual, because
it does not allow us to use α at other Type levels of the universe
hierarchy (Type_1, Type_2, ...).
Interestingly, if we assume that `Quotient` exists, we can pretty much
encode any HIT using it. E.g.
type Exp Γ τ
| var (x : String) ((x : τ) ∈ Γ) : Exp Γ τ
| app (Exp Γ (τ₁ → τ₂)) (Exp Γ τ₁) : Exp Γ τ₂
| lam (x : String) (Exp ((x : τ₁) · Γ) τ₂) : Exp Γ (τ₁ → τ₂)
where α-equiv : y ∉ fv e → lam x e = lam y (e[y/x]);
can be encoded using Quotient as:
Exp' : ?G → ?T → Type;
Rexp : Exp' ?Γ ?τ → Exp' ?Γ ?τ → Type;
Exp Γ τ = Quotient (Exp' Γ τ) (Rexp Γ τ);
type Exp' Γ τ
| var' (x : String) ((x : τ) ∈ Γ) : Exp' Γ τ
| app' (Exp Γ (τ₁ → τ₂)) (Exp Γ τ₁) : Exp' Γ τ₂
| lam' (x : String) (Exp ((x : τ₁) · Γ) τ₂) : Exp' Γ (τ₁ → τ₂);
var x P = Qin (var' x P);
app e₁ e₂ = Qin (app' e₁ e₂);
lam x e = Qin (lam' x e);
type Rexp Γ τ x y
| rα-equiv (y ∉ fv e) : Rexp (lam x e) (lam y (subst e x (var y)))
α-equiv x = Qeq (rα-equiv x);
But the termination checker (which here will check the non-negative
occurrence of `Exp` in its definition) needs to handle `Quotient` as
a "positive constructor", and that likely needs to be done in an
ad-hoc way.
So, I think I can add HITs to Typer without really modifying the
inductive types, and instead providing a built-in Quotient type, and
teaching the termination checker about it.
Stefan