The MRC program

nurtures early-career mathematicians—those who are close to finishing their doctorates or have recently finished—and provides them with opportunities to build social and collaborative networks to inspire and sustain each other in their work.

MRCs are held in the “breathtaking mountain setting” of Snowbird Resort in Utah. The HoTT MRC will be organized by Dan Christensen, Chris Kapulkin, Dan Licata, Emily Riehl, and myself. From the description:

The goal of this workshop is to bring together advanced graduate students and postdocs having some background in one (or more) areas such as algebraic topology, category theory, mathematical logic, or computer science, with the goal of learning how these areas come together in homotopy type theory, and working together to prove new results. Basic knowledge of just one of these areas will be sufficient to be a successful participant.

So if you are within a few years of your Ph.D. on either side, and are interested in HoTT, please consider applying! I think this has the potential to be a really exciting week, and a really great way to “jump-start” a research program in HoTT or related to it. Even though the application deadline isn’t until March 1, we would appreciate it for planning purposes if interested folks could apply as soon as possible. (The majority of places are for U.S. citizens or those affiliated with U.S. institutions, though there may be space for a few international participants. Women and underrepresented minorities are especially encouraged to apply.)

There are a lot of things that might happen at this workshop. There is a general list of topics posted with the description, and as the date approaches we’ll make further plans depending on our participants and their backgrounds (which is one of the reasons we want you to apply now). One topic that I think is a good candidate for quick progress is synthetic homotopy theory, where I suspect there’s still a lot of low-hanging fruit ready to be picked by collaborations between people familiar with classical homotopy theory and people with more experience thinking type-theoretically. Another topic that’s less of a sure thing, but that I am really hoping to get more people working on, is the problem of semantics for univalence: although I’ve about exhausted my own ideas in this direction, I still have hopes that there are model categories with strict univalent universes out there that present all -toposes, which might be found by fresh eyes. And, as you can see, there are plenty of other potential topics as well.

Feel free to ask any questions of me or any of the organizers.

]]>

SQL is the lingua franca for retrieving structured data. Existing semantics for SQL, however, either do not model crucial features of the language (e.g., relational algebra lacks bag semantics, correlated subqueries, and aggregation), or make it hard to formally reason about SQL query rewrites (e.g., the SQL standard’s English is too informal). This post focuses on the ways that HoTT concepts (e.g., Homotopy Types, the Univalence Axiom, and Truncation) enabled us to develop HoTTSQL — a new SQL semantics that makes it easy to formally reason about SQL query rewrites. Our paper also details the rich set of SQL features supported by HoTTSQL.

You can download this blog post’s source (implemented in Coq using the HoTT library). Learn more about HoTTSQL by visiting our website.

The basic datatype in SQL is a relation, which is a *bag* (i.e., multiset) of tuples with the same given schema. You can think of a tuple’s schema as being like a variable’s type in a programming language. We formalize a bag of some type A as a function that maps every element of A to a type. The type’s cardinality indicates how many times the element appears in the bag.

Definition Bag A := A -> Type.

For example, the bag numbers = {| 7, 42, 7 |} can be represented as:

Definition numbers : Bag nat :=

fun n => match n with

| 7 => Bool

| 42 => Unit

| _ => Empty

end.

A SQL query maps one or more input relations to an output relation. We can implement SQL queries as operations on bags. For example, a disjoint union query in SQL can be implemented as a function that takes two input bags r1 and r2, and returns a bag in which every tuple a appears r1 a + r2 a times. Note that the cardinality of the sum type r1 a + r2 a is equal to the sum of the cardinalities of r1 a and r2 a.

Definition bagUnion {A} (r1 r2:Bag A) : Bag A :=

fun (a:A) => r1 a + r2 a.

Most database systems contain a query optimizer that applies SQL rewrite rules to improve query performance. We can verify SQL rewrite rules by proving the equality of two bags. For example, we can show that the union of r1 and r2 is equal to the union of r2 and r1, using functional extensionality (by_extensionality), the univalence axiom (path_universe_uncurried), and symmetry of the sum type (equiv_sum_symm).

Lemma bag_union_symm {A} (r1 r2 : Bag A) :

bagUnion r1 r2 = bagUnion r2 r1.

Proof.

unfold bagUnion.

by_extensionality a.

(* r1 a + r1 a = r2 a + r2 a *)

apply path_universe_uncurried.

(* r1 a + r1 a <~> r2 a + r2 a *)

apply equiv_sum_symm.

Qed.

Note that + and * on homotopy types are *like* the operations of a commutative semi-ring, Empty and Unit are *like* the identity elements of a commutative semi-ring, and there are paths witnessing the commutative semi-ring axioms for these operations and identity elements. We use the terminology *like* here, because algebraic structures over higher-dimensional types in HoTT are usually defined using coherence conditions between the equalities witnessing the structure’s axioms, which we have not yet attempted to prove.

Many SQL rewrite rules simplify to an expressions built using the operators of this semi-ring (e.g. r1 a + r1 a = r2 a + r2 a above), and could thus be potentially solved or simplified using a ring tactic (see). Unfortunately, Coq’s ring tactic is not yet ported to the HoTT library. Porting ring may dramatically simplify many of our proofs (Anyone interested in porting the ring tactic? Let us know).

It is reasonable to assume that SQL relations are bags that map tuples only to 0-truncated types (types with no higher homotopical information), because real-world databases’ input relations only contain tuples with finite multiplicity (Fin n is 0-truncated), and because SQL queries only use type operators that preserve 0-truncation. However, HoTTSQL does not requires this assumption, and as future work, it may be interesting to understand what the “cardinality” of a type with higher homotopical information means.

How to model bags is a fundamental design decision for mechanizing formal proofs of SQL query equivalences. Our formalization of bags is unconventional but effective for reasoning about SQL query rewrites, as we will see.
## Schemas

Previous work has modeled bags as *lists* (e.g., as done by Malecha et al.), where SQL queries are recursive functions over input lists, and two bags are equal iff their underlying lists are equal up to element reordering. Proving two queries equal thus requires induction on input lists (including coming up with induction hypothesis) and reasoning about list permutations. In contrast, by modeling bags as functions from tuples to types, proving two queries equal just requires proving the equality of two HoTT types.

In the database research community, prior work has modeled bags as *functions to natural numbers* (e.g., as done by Green et al.). Using this approach, one cannot define the potentially infinite sum ∑ a, r a that counts the number of elements in a bag r. This is important since a basic operation in SQL, projection, requires counting all tuples in a bag that match a certain predicate. In contrast, by modeling bags as functions from tuples to types, we can count the number of elements in a bag using the sigma type ∑, where the cardinality of the sigma type ∑ a, r a is equal to the sum of the cardinalities of r a for all a.

Traditionally, a relation is modeled as a bag of n-ary tuples, and a relation’s *schema* both describes how many elements there are in each tuple (i.e., n), and the the type of each element. Thus, a schema is formalized as a list of types.

In HoTTSQL, a relation is modeled as a bag of nested pairs (nested binary-tuples), and a relation’s schema both describes the nesting of the pairs and the types of the leaf pairs. In HoTTSQL, a schema is thus formalized as a binary tree, where each node stores only its child nodes, and each leaf stores a type. Our formalization of schemas as trees and tuples as nested pairs is unconventional. We will see later how this choice simplifies reasoning.

Inductive Schema :=

| node (s1 s2 : Schema)

| leaf (T:Type)

.

For example, a schema for people (with a name, age, and employment status) can be expressed as Person : Schema := node (leaf Name) (node (leaf Nat) (leaf Bool)).

We formalize a *tuple* as a function Tuple that takes a schema s and returns a nested pair which matches the tree structure and types of s.

Fixpoint Tuple (s:Schema) : Type :=

match s with

| node s1 s2 => Tuple s1 * Tuple s2

| leaf T => T

end.

For example, Tuple Person = Name * (Nat * Bool) and (Alice, (23, false)) : Tuple Person.

Finally, we formalize a *relation* as a bag of tuples that match a given schema s.

Definition Relation (s:Schema) := Bag (Tuple s).

Recall that a SQL query maps one or more input relations to an output relation, and that we can implement SQL queries with operations on bags. In this section, we incrementally introduce various SQL queries, and describe their semantics in terms of bags.

The following subset of the SQL language supports unioning relations, and selecting (i.e., filtering) tuples in a relation.

Inductive SQL : Schema -> Type :=

| union {s} : SQL s -> SQL s -> SQL s

| select {s} : Pred s -> SQL s -> SQL s

(* … *)

.

Fixpoint denoteSQL {s} (q : SQL s) : Relation s :=

match q with

| union _ q1 q2 => fun t => denoteSQL q1 t + denoteSQL q2 t

| select _ b q => fun t => denotePred b t * denoteSQL q t

(* … *)

end.

The query select b q removes all the tuples from the relation returned by the query q where the predicate b does not hold. We denote the predicate as a function denotePred(b) : Tuple s -> Type that maps a tuple to a (-1)-truncated type. denotePred(b) t is Unit if the predicate holds for t, and Empty otherwise. The query multiplies the relation with the predicate to implement the semantics of the query (i.e., n * Unit = n and n * Empty = Empty, where n is the multiplicity of the input tuple t).

To syntactically resemble SQL, we write q1 UNION ALL q2 for union q1 q2, q WHERE b for select b q, and SELECT * FROM q for q. We write ⟦q⟧ for the denotation of a query denoteQuery q, and ⟦b⟧ for the denotation of a predicate denotePred b.

To prove that two SQL queries are equal, one has to prove that their two denotations are equal, i.e., that two bags returned by the two queries are equal, given any input relation(s). The following example shows how we can prove that selection distributes over union, by reducing it to showing the distributivity of * over + (lemma sum_distrib_l).

Lemma proj_union_distr s (q1 q2 : SQL s) (p:Pred s) :

⟦ SELECT * FROM (q1 UNION ALL q2) WHERE p ⟧ =

⟦ (SELECT * FROM q1 WHERE p) UNION ALL

(SELECT * FROM q2 WHERE p) ⟧.

Proof.

simpl.

by_extensionality t.

(* ⟦p⟧ t * (⟦q1⟧ t + ⟦q2⟧ t) = ⟦p⟧ t * ⟦q1⟧ t + ⟦p⟧ t * ⟦q2⟧ t *)

apply path_universe_uncurried.

apply sum_distrib_l.

Qed.

So far, we have seen the use of homotopy types to model SQL relations, and have seen the use of the univalence axiom to prove SQL rewrite rules. We now show the use of truncation to model the removal of duplicates in SQL relations. To show an example of duplicate removal in SQL, we first have to extend our semantics of the SQL language with more features.

Inductive Proj : Schema -> Schema -> Type :=

| left {s s’} : Proj (node s s’) s

| right {s s’} : Proj (node s’ s) s

(* … *)

.

Inductive SQL : Schema -> Type :=

(* … *)

| distinct {s} : SQL s -> SQL s

| product {s1 s2} : SQL s1 -> SQL s2 -> SQL (node s1 s2)

| project {s s’} : Proj s s’ -> SQL s -> SQL s’

(* … *)

.

Fixpoint denoteProj {s s’} (p : Proj s s’) : Tuple s ->

Tuple s’ :=

match p with

| left _ _ => fst

| right _ _ => snd

(* … *)

end.

Fixpoint denoteSQL {s} (q : SQL s) : Relation s :=

match q with

(* … *)

| distinct _ q => fun t => ║ denoteSQL q t ║

| product _ _ q1 q2 => fun t => denoteSQL q1 (fst t) *

denoteSQL q2 (snd t)

| project _ _ p q => fun t’ => ∑ t, denoteSQL q t *

(denoteProj p t = t’)

(* … *)

end.

The query distinct q removes duplicate tuples in the relation returned by the query q using the (-1)-truncation function ║ q ║ (see HoTT book, chapter 3.7).

The query product q1 q2 creates the cartesian product of q1 and q2, i.e., it returns a bag that maps every tuple consisting of two tuples t1 and t2 to the number of times t1 appears in q1 multiplied by the number of times t2 appears in q2.

The query project p q projects elements from each tuple contained in the query q. The projection is defined by p, and is denoted as a function that takes a tuple of some schema s and returns a new tuple of some schema s’. For example, left is the projection that takes a tuple and returns the tuple’s first element. We assume that tuples have no higher homotopical information, and that equality between tuples is thus (-1)-truncated.

Like before, we write DISTINCT q for distinct q, FROM q1, q2 for product q1 q2, and SELECT p q for project p q. We write ⟦p⟧ for the denotation of a projection denoteProj p.

Projection of products is the reason HoTTSQL must model schemas as nested pairs. If schemas were flat n-ary tuples, the left projection would not know which elements of the tuple formerly belonged to the left input relation of the product, and could thus not project them (feel free to contact us if you have ideas on how to better represent schemas)

Projection requires summing over all tuples in a bag, as multiple tuples may be merged into one. This sum is over an infinite domain (all tuples) and thus cannot generally be implemented with natural numbers. Implementing it using the ∑ (sigma) type is however trivial.

Equipped with these additional features, we can now prove the following rewrite rule.

Lemma self_join s (q : SQL s) :

⟦ DISTINCT SELECT left FROM q, q ⟧ =

⟦ DISTINCT SELECT * FROM q ⟧.

The two queries are equal, because the left query performs a redundant self-join. Powerful database query optimizations, such as magic sets rewrites and conjunctive query equivalences are based on redundant self-joins elimination.

To prove the equivalence of any two (-1)-truncated types ║ q1 ║ and ║ q2 ║, it suffices to prove the bi-implication q1 <-> q2 (lemma equiv_iff_trunc). This is one of the cases where concepts from HoTT simplify formal reasoning in a big way. Instead of having to apply a series of equational rewriting rules (which is complicated by the fact that they need to be applied under the variable bindings of Σ), we can prove the goal using deductive reasoning.

Proof.

simpl.

by_extensionality t.

(* ║ ∑ t’, ⟦q⟧ (fst t’) * ⟦q⟧ (snd t’) * (fst t’ = t) ║ =

║ ⟦q⟧ t ║ *)

apply equiv_iff_trunc.

split.

– (* ∃ t’, ⟦q⟧ (fst t’) ∧ ⟦q⟧ (snd t’) ∧ (fst t’ = t) →

⟦q⟧ t *)

intros [[t1 t2] [[h1 h2] eq]].

destruct eq.

apply h1.

– (* ⟦q⟧ t →

∃ t’, ⟦q⟧ (fst t’) ∧ ⟦q⟧ (snd t’) ∧ (fst t’ = t) *)

intros h.

exists (t, t).

(* ⟦q⟧ t ∧ ⟦q⟧ t ∧ (t = t) *)

split; [split|].

+ apply h.

+ apply h.

+ reflexivity.

The queries in the above rewrite rule fall in the well-studied category of conjunctive queries where equality is decidable (while equality between arbitrary SQL queries is undecidable). Using Coq’s support for automating deductive reasoning (with Ltac), we have implemented a decision procedure for the equality of conjunctive queries (it’s only 40 lines of code; see this posts source for details), the aforementioned rewrite rule can thus be proven in one line of Coq code.

Restart.

conjuctiveQueryProof.

Qed.

We have shown how concepts from HoTT have enabled us to develop HoTTSQL, a SQL semantics that makes it easy to formally reason about SQL query rewrites.

We model bags of type A as a function A -> Type. Bags can be proven equal using the univalence axiom. In contrast to models of bags as list A, we require no inductive or permutation proofs. In contrast to models of bags as A -> nat, we can count the number of elements in any bag.

Duplicate elimination in SQL is implemented using (-1)-truncation, which leads to clean and easily automatable deductive proofs. Many of our proofs could be further simplified with a ring tactic for the 0-trucated type semi-ring.

Visit our website to access our source code, learn how we denote other advanced SQL features such as correlated subqueries, aggregation, advanced projections, etc, and how we proved complex rewrite rules (e.g., magic set rewrites).

Contact us if you have any question, feedback, or know how to improve HoTTSQL (e.g., you know how to use more concepts from HoTT to extend HoTTSQL).

]]>

My dissertation was on the topic of combinatorial species, and specifically on the idea of using species as a foundation for thinking about generalized notions of algebraic data types. (Species are sort of dual to containers; I think both have intereseting and complementary things to offer in this space.) I didn’t really end up getting very far into practicalities, instead getting sucked into a bunch of more foundational issues.

To use species as a basis for computational things, I wanted to first “port” the definition from traditional, set-theory-based, classical mathematics into a constructive type theory. HoTT came along at just the right time, and seems to provide exactly the right framework for thinking about a constructive encoding of combinatorial species.

For those who are familiar with HoTT, this post will contain nothing all that new. But I hope it can serve as a nice example of an “application” of HoTT. (At least, it’s more applied than research in HoTT itself.)

Traditionally, a species is defined as a functor , where is the groupoid of finite sets and bijections, and is the category of finite sets and (total) functions. Intuitively, we can think of a species as mapping finite sets of “labels” to finite sets of “structures” built from those labels. For example, the species of linear orderings (*i.e.* lists) maps the finite set of labels to the size- set of all possible linear orderings of those labels. Functoriality ensures that the specific identity of the labels does not matter—we can always coherently relabel things.

So what happens when we try to define species inside a constructive type theory? The crucial piece is : the thing that makes species interesting is that they have built into them a notion of bijective relabelling, and this is encoded by the groupoid . The first problem we run into is how to encode the notion of a *finite* set, since the notion of finiteness is nontrivial in a constructive setting.

One might well ask why we even care about finiteness in the first place. Why not just use the groupoid of *all* sets and bijections? To be honest, I have asked myself this question many times, and I still don’t feel as though I have an entirely satisfactory answer. But what it seems to come down to is the fact that species can be seen as a categorification of generating functions. Generating functions over the semiring can be represented by functions , that is, each natural number maps to some coefficient in ; each natural number, categorified, corresponds to (an equivalence class of) *finite* sets. Finite label sets are also important insofar as our goal is to actually use species as a basis for *computation*. In a computational setting, one often wants to be able to do things like enumerate all labels (*e.g.* in order to iterate through them, to do something like a map or fold). It will therefore be important that our encoding of finiteness actually has some computational content that we can use to enumerate labels.

Our first attempt might be to say that a finite set will be encoded as a type together with a bijection between and a canonical finite set of a particular natural number size. That is, assuming standard inductively defined types and ,

However, this is unsatisfactory, since defining a suitable notion of bijections/isomorphisms between such finite sets is tricky. Since is supposed to be a groupoid, we are naturally led to try using equalities (*i.e.* paths) as morphisms—but this does not work with the above definition of finite sets. In , there are supposed to be different morphisms between any two sets of size . However, given any two same-size inhabitants of the above type, there is only *one* path between them—intuitively, this is because paths between -types correspond to tuples of paths relating the components pointwise, and such paths must therefore preserve the *particular* relation to . The only bijection which is allowed is the one which sends each element related to to the other element related to , for each .

So elements of the above type are not just finite sets, they are finite sets *with a total order*, and paths between them must be order-preserving; this is too restrictive. (However, this type is not without interest, and can be used to build a counterpart to L-species. In fact, I think this is exactly the right setting in which to understand the relationship between species and L-species, and more generally the difference between isomorphism and *equipotence* of species; there is more on this in my dissertation.)

We can fix things using propositional truncation. In particular, we define

That is, a “finite set” is a type together with some *hidden* evidence that is equivalent to for some . (I will sometimes abuse notation and write instead of .) A few observations:

- First, we can pull the size out of the propositional truncation, that is, . Intuitively, this is because if a set is finite, there is only one possible size it can have, so the evidence that it has that size is actually a mere proposition.
- More generally, I mentioned previously that we sometimes want to use the computational evidence for the finiteness of a set of labels,
*e.g.*enumerating the labels in order to do things like maps and folds. It may seem at first glance that we cannot do this, since the computational evidence is now hidden inside a propositional truncation. But actually, things are exactly the way they should be: the point is that we can use the bijection hidden in the propositional truncation*as long as the result does not depend on the particular bijection we find there*. For example, we cannot write a function which returns the value of type corresponding to , since this reveals something about the underlying bijection; but we can write a function which finds the smallest value of (with respect to some linear ordering), by iterating through all the values of and taking the minimum. - It is not hard to show that if , then is a set (
*i.e.*a 0-type) with decidable equality, since is equivalent to the 0-type . Likewise, itself is a 1-type. - Finally, note that paths between inhabitants of now do exactly what we want: a path is really just a path between 0-types, that is, a bijection, since trivially.

We can now define species in HoTT as functions of type . The main reason I think this is the Right Definition ™ of species in HoTT is that functoriality comes for free! When defining species in set theory, one must say “a species is a functor, *i.e.* a pair of mappings satisfying such-and-such properties”. When constructing a particular species one must explicitly demonstrate the functoriality properties; since the mappings are just functions on sets, it is quite possible to write down mappings which are not functorial. But in HoTT, all functions are functorial with respect to paths, and we are using paths to represent the morphisms in , so any function of type automatically has the right functoriality properties—it is literally impossible to write down an invalid species. Actually, in my dissertation I define species as functors between certain categories built from and , but the point is that any function can be automatically lifted to such a functor.

Here’s another nice thing about the theory of species in HoTT. In HoTT, coends whose index category are groupoids are just plain -types. That is, if is a groupoid, a category, and , then . In set theory, this coend would be a *quotient* of the corresponding -type, but in HoTT the isomorphisms of are required to correspond to paths, which automatically induce paths over the -type which correspond to the necessary quotient. Put another way, we can define coends in HoTT as a certain HIT, but in the case that is a groupoid we already get all the paths given by the higher path constructor anyway, so it is redundant. So, what does this have to do with species, I hear you ask? Well, several species constructions involve coends (most notably partitional product); since species are functors from a groupoid, the definitions of these constructions in HoTT are particularly simple. We again get the right thing essentially “for free”.

There’s lots more in my dissertation, of course, but these are a few of the key ideas specifically relating species and HoTT. I am far from being an expert on either, but am happy to entertain comments, questions, etc. I can also point you to the right section of my dissertation if you’re interested in more detail about anything I mentioned above.

]]>

In a typical functional programming career, at some point one encounters the notions of parametricity and free theorems.

Parametricity can be used to answer questions such as: is every function

f : forall x. x -> x

equal to the identity function? Parametricity tells us that this is true for System F.

However, this is a metatheoretical statement. Parametricity gives properties about the *terms* of a language, rather than proving *internally* that certain elements satisfy some properties.

So what can we prove internally about a polymorphic function ?

In particular, we can see that internal proofs (claiming that must be the identity function for every type ) *cannot* exist: exercise 6.9 of the HoTT book tells us that, assuming LEM, we can exhibit a function such that is (Notice that the proof of this is not quite as trivial as it may seem: LEM only gives us if is a (mere) proposition (a.k.a. subsingleton). Hence, simple case analysis on does not work, because this is not necessarily a proposition.)

And given the fact that LEM is consistent with univalent foundations, this means that a proof that is the identity function cannot exist.

I have proved that LEM is exactly what is needed to get a polymorphic function that is not the identity on the booleans.

**Theorem.** If there is a function with then LEM holds.

If then by simply trying both elements we can find an explicit boolean such that Without loss of generality, we can assume

For the remainder of this analysis, let be an arbitrary proposition. Then we want to achieve to prove LEM.

We will consider a type with three points, where we identify two points depending on whether holds. In other words, we consider the quotient of a three-element type, where the relation between two of those points is the proposition

I will call this space and it can be defined as where is the *suspension* of This particular way of defining the quotient, which is equivalent to a quotient of a three-point set, will make case analysis simpler to set up. (Note that suspensions are not generally quotients: we use the fact that is a proposition here.)

Notice that if holds, then and also

We will consider at the type (*not* itself!). Now the proof continues by defining

(where is the equivalence given by the identity function on ) and doing case analysis on and if necessary also on for some elements I do not believe it is very instructive to spell out all cases explicitly here. I wrote a more detailed note containing an explicit proof.

Notice that doing case analysis here is simply an instance of the induction principle for In particular, we do not require decidable equality of (which would already give us which is exactly what we are trying to prove).

For the sake of illustration, here is one case:

- Assume holds. Then since then by transporting along an appropriate equivalence (namely the one that identifies with we get But since is an equivalence for which is a fixed point, must be the identity everywhere, that is, which is a contradiction.

I formalized this proof in Agda using the HoTT-Agda library

Thanks to Martín Escardó, my supervisor, for his support. Thanks to Uday Reddy for giving the talk on parametricity that inspired me to think about this.

]]>

- the small library about colimits that I formalized in Coq,
- a construction of the image of a function as a colimit, which is essentially a sliced version of the result that Floris van Doorn talked in this blog recently, and further improvements.

I present my hott-colimits library in the first part. This part is quite easy but I hope that the library could be useful to some people. The second part is more original. Lets sketch it.

Given a function we can construct a diagram

where the HIT is defined by:

HIT KP f := | kp : A -> KP f | kp_eq : forall x x', f(x) = f(x') -> kp(x) = kp(x').

and where is defined recursively from . We call this diagram the iterated kernel pair of . The result is that the colimit of this diagram is , the image of ( is the homotopy fiber of in ).

It generalizes Floris’ result in the following sense: if we consider the unique arrow (where is Unit) then is the one-step truncation of and the colimit is equivalent to the truncation of .

We then go further. Indeed, this HIT doesn’t respect the homotopy levels at all: even is the circle. We try to address this issue considering an HIT that take care of already existing paths:

HIT KP' f := | kp : A -> KP' f | kp_eq : forall x x', f(x) = f(x') -> kp(x) = kp(x'). | kp_eq_1 : forall x, kp_eq (refl (f x)) = refl (kp x)

This HIT avoid adding new paths when some elements are already equals, and turns out to better respect homotopy level: it at least respects hProps. See below for the details.

Besides, there is another interesting thing considering this HIT: we can sketch a link between the iterated kernel pair using and the Čech nerve of a function. We outline this in the last paragraph.

All the following is joint work with Kevin Quirin and Nicolas Tabareau (from the CoqHoTT project), but also with Egbert Rijke, who visited us.

All our results are formalized in Coq. The library is available here:

https://github.com/SimonBoulier/hott-colimits

In homotopy type theory, Type, the type of all types can be seen as an ∞-category. We seek to calculate some homotopy limits and colimits in this category. The article of Jeremy Avigad, Krzysztof Kapulkin and Peter LeFanu Lumsdaine explain how to calculate the limits over graphs using sigma types. For instance an equalizer of two function and is .

The colimits over graphs are computed in same way with Higher Inductive Types instead of sigma types. For instance, the coequalizer of two functions is

HIT Coeq (f g: A -> B) : Type := | coeq : B -> Coeq f g | cp : forall x, coeq (f x) = coeq (g x).

In both case there is a severe restriction: we don’t know how two compute limits and colimits over diagrams which are much more complicated than those generated by some graphs (below we use an extension to “graphs with compositions” which is proposed in the exercise 7.16 of the HoTT book, but those diagrams remain quite poor).

We first define the type of graphs and diagrams, as in the HoTT book (exercise 7.2) or in hott-limits library of Lumsdaine *et al.*:

Record graph := { G_0 :> Type ; G_1 :> G_0 -> G_0 - Type }.

Record diagram (G : graph) := { D_0 :> G -> Type ; D_1 : forall {i j : G}, G i j -> (D_0 i -> D_0 j) }.

And then, a cocone over a diagram into a type :

Record cocone {G: graph} (D: diagram G) (Q: Type) := { q : forall (i: G), D i - X ; qq : forall (i j: G) (g: G i j) (x: D i), q j (D_1 g x) = q i x }.

Let be a cocone into and be a function . Then we can extend to a cocone into by postcomposition with . It gives us a function

A cocone is said to be universal if, for all other cocone over the same diagram, can be obtained uniquely by extension of , that we translate by:

Definition is_universal (C: cocone D Q) := forall (Q': Type), IsEquiv (postcompose_cocone C Q').

Last, a type is said to be a colimit of the diagram if there exists a universal cocone over into .

The existence of the colimit over a diagram is given by the HIT:

HIT colimit (D: diagram G) : Type := | colim : forall (i: G), D i - colimit D | eq : forall (i j: G) (g: G i j) (x: D i), colim j (D_1 g x) = colim i x

Of course, is a colimit of .

Let and be two diagrams over the same graph . A morphism of diagrams is defined by:

Record diagram_map (D1 D2 : diagram G) := { map_0: forall i, D1 i - D2 i ; map_1: forall i j (g: G i j) x, D_1 D2 g (map_0 i x) = map_0 j (D_1 D1 g x) }.

We can compose diagram morphisms and there is an identity morphism. We say that a morphism is an equivalence of diagrams if all functions are equivalences. In that case, we can define the inverse of (reversing the proofs of commutation), and check that it is indeed an inverse for the composition of diagram morphisms.

We yet defined forward extension of a cocone by postcomposition, we now define backward extension. Given a diagram morphism , we can make every cocone over into a cocone over by precomposition by . It gives us a function

We check that precomposition and postcomposition respect the identity and the composition of morphism. And then, we can show that the notions of universality and colimits are stable by equivalence.

Let be a diagram morphism and and two colimits of and . Let’s note and the universal cocone into and . Then, we can get a function given by:

We check that if is an equivalence of diagram then the function given by is well an inverse of .

As a consequence, we get:

The colimits of two equivalents diagrams are equivalent.

In particular, if we consider the identity morphism we get:

Let and be two colimits of the same diagram, then: .

So, if we assume univalence, the colimit of a diagram is truly unique!

Let be a type and, for all , a diagram over a graph . We can then build a new diagram over whose objects are the and functions are induced by the identity on the first component and by on the second one. Let’s note this diagram.

Seemingly, from a family of cocone , we can make a cocone over into .

We proved the following result, which we believed to be quite nice:

If, for all , is a colimit of , then is a colimit of .

Let’s first recall the result of Floris. An attempt to define the propositional truncation is the following:

HIT {_} (A: Type) := | α : A -> {A} | e : forall (x x': A), α x = α x'.

Unfortunately, in general is not a proposition, the path constructor is not strong enough. But we have the following result:

Let be a type. Let’s consider the following diagram:

Then, is a colimit of this diagram.

Let’s generalize this result to a function (we will recover the theorem considering the unique function ).

Let . We note the colimit of the kernel pair of :

where the pullback is given by .

Hence, is the following HIT:

Inductive KP f := | kp : A -> KP f | kp_eq : forall x x', f(x) = f(x') -> kp(x) = kp(x').

Let’s consider the following cocone:

we get a function by universality (another point of view is to say that is defined by ).

Then, iteratively, we can construct the following diagram:

where and .

The iterated kernel pair of is the subdiagram

We proved the following result:

The colimit of this diagram is , the image of .

The proof is a slicing argument to come down to Floris’ result. It uses all properties of colimits that we talked above. The idea is to show that those three diagrams are equivalent.

Going from the first line to the second is just apply the equivalence (for ) at each type. Going from the second to the third is more involved, we don’t detail it here. And is well the colimit of the last line: by commutation with sigmas it is sufficient to show that for all , is the colimit of the diagram

which is exactly Floris’ result!

The details are available here.

The previous construction has a small defect: it did not respect the homotopy level at all. For instance is the circle . Hence, to compute (which is of course), we go through very complex types.

We found a way to improve this: adding identities!

Indeed, the proof keeps working if we replace by which is defined by:

Inductive KP' f := | kp : A -> KP' f | kp_eq : forall x x', f(x) = f(x') -> kp(x) = kp(x'). | kp_eq_1 : forall x, kp_eq (refl (f x)) = refl (kp x)

can be seen as a “colimit with identities” of the following diagram :

(♣)

with .

In his article, Floris explains that, when then and are not equal. But now they become equal: by path induction we bring back to . That is, if two elements are already equal, we don’t add any path between them.

And indeed, this new HIT respects the homotopy level better, at least in the following sense:

- is (meaning that the one-step truncation of a contractible type is now ),
- If is an embedding (in the sense that is an equivalence for all ) then so is . In particular, if is hProp then so is (meaning that the one-step truncation of an hProp is now itself).

Although we don’t succeed in making it precise, there are several hints which suggest a link between the iterated kernel pair and the Čech nerve of a function.

The Čech nerve of a function is a generalization of his kernel pair: it is the simplicial object

(the degeneracies are not dawn but they are present).

We will call n-truncated Čech nerve the diagram restricted to the n+1 first objects:

(degeneracies still here).

The kernel pair (♣) is then the 1-truncated Čech nerve.

We wonder to which extent could be the colimit of the (n+1)-truncated Čech nerve. We are far from having such a proof but we succeeded in proving :

- That is the colimit of the kernel pair (♣),
- and that there is a cocone over the 2-trunated Čech nerve into

(both in the sense of “graphs with compositions”, see exercise 7.16 of the HoTT book).

The second point is quite interesting because it makes the path concatenation appear. We don’t detail exactly how, but to build a cocone over the 2-trunated Čech nerve into a type , must have a certain compatibility with the path concatenation. doesn’t have such a compatibility: if and , in general we do **not** have

in .

On the contrary, **have** the require compatibility: we can prove that

in .

( has indeed the type because is and then .)

This fact is quite surprising. The proof is basically getting an equation with a transport with apD and then making the transport into a path concatenation (see the file *link_KPv2_CechNerve.v* of the library for more details).

Many questions are left opened. To what extent is linked with the (n+1)-truncated diagram? Could we use the idea of the iterated kernel pair to define a groupoid object internally? Indeed, in an ∞-topos every groupoid object is effective (by Giraud’s axioms) an then is the Čech nerve of his colimit…

]]>

You can install it on Windows, OS X or Linux. It will come with a useful mode for Emacs, with syntax highlighting, on-the-fly syntax checking, autocompletion and many other features. There is also an online version of Lean which you can try in your browser. The on-line version is quite a bit slower than the native version and it takes a little while to load, but it is still useful to try out small code snippets. You are invited to test the code snippets in this post in the on-line version. You can run code by pressing shift+enter.

In this post I’ll first say more about the Lean proof assistant, and then talk about the considerations for the HoTT library of Lean (Lean has two libraries, the standard library and the HoTT library). I will also cover our approach to higher inductive types. Since Lean is not mature yet, things mentioned below can change in the future.

Update January 2017: the newest version of Lean currently doesn’t support HoTT, but there is a frozen version which does support HoTT. The newest version is available here, and the frozen version is available here. To use the frozen version, you will have to compile it from the source code yourself.

**Examples**

First let’s go through some examples. Don’t worry if you don’t fully understand these examples for now, I’ll go over the features in more detail below. You can use the commands `check`

, `print`

and `eval`

to ask Lean for information. I will give the output as a comment, which is a double dash in Lean. `check`

just gives the type of an expression.

check Σ(x y : ℕ), x + 5 = y -- Σ (x y : ℕ), x + 5 = y : Type₀

This states that this sigma-type lives in the lowest universe in Lean (since `ℕ`

and `le`

live in the lowest universe). Unicode can be input (in both the browser version and the Emacs version) using a backslash. For example `Σ`

is input by ** \Sigma** or

`\S`

`check`

is useful to see the type of a theorem (the curly braces indicate that those arguments are implicit).

check @nat.le_trans -- nat.le_trans : Π {n m k : ℕ}, n ≤ m → m ≤ k → n ≤ k

`print`

can show definitions.

open eq print inverse -- definition eq.inverse : Π {A : Type} {x y : A}, x = y → y = x := -- λ (A : Type) (x : A), eq.rec (refl x)

It prints which notation uses a particular symbol.

print × -- _ `×`:35 _:34 := prod #1 #0

And it prints inductive definitions.

print bool -- inductive bool : Type₀ -- constructors: -- bool.ff : bool -- bool.tt : bool

`eval`

evaluates an expression.

eval (5 + 3 : ℕ) -- 8

**The Kernel**

Lean is a proof assistant with as logic dependent type theory with inductive types and universes, just as Coq and Agda. It has a small kernel, which implements only the following components:

- Dependent lambda calculus
- Universe polymorphism in a hierarchy of many universe levels
- Inductive types and inductive families of types, generating only the recursor for an inductive type.

In particular it does **not** contain

- A Termination checker
- Fixpoint operators
- Pattern matching
- Module management and overloading

Let’s discuss the above features in more detail. The function types are exactly as in the book with judgemental beta and eta rules. There are a lot of different notations for functions and function types, for example

open nat variables (A B : Type) (P : A → Type) (Q : A → B → Type) (f : A → A) check A -> B check ℕ → ℕ check Π (a : A), P a check Pi a, P a check ∀a b, Q a b check λ(n : ℕ), 2 * n + 3 check fun a, f (f a)

Lean supports universe polymorphism. So if you define the identity function as follows

definition my_id {A : Type} (a : A) : A := a

you actually get a function `my_id.{u}`

for every universe `u`

. You rarely have to write universes explicitly, but you can always give them explicitly if you want. For example:

definition my_id.{u} {A : Type.{u}} (a : A) : A := a set_option pp.universes true check sum.{7 5} -- sum.{7 5} : Type.{7} → Type.{5} → Type.{7}

The universes in Lean are *not cumulative*. However, with universe polymorphism universe cumulativity is rarely needed. In the cases where you would use universe cumulativity you can use the `lift`

map explicitly. There are only a handful of definitions or theorems (less than 10) currently in the HoTT library where lifts are needed (if it is not proving something about lifts itself). Some examples:

- In the proof that univalence implies function extensionality;
- In the proof that
`Type.{u}`

is not a set, given that`Type.{0}`

is not a set; - In the Yoneda Lemma;
- In the characterization of equality in sum types (see below for the pattern matching notation):
open sum definition sum.code.{u v} {A : Type.{u}} {B : Type.{v}} : A + B → A + B → Type.{max u v} | sum.code (inl a) (inl a') := lift (a = a') | sum.code (inr b) (inr b') := lift (b = b') | sum.code _ _ := lift empty

The first two examples only need lifts because we also decided that inductive types without parameters (`empty`

, `unit`

, `bool`

, `nat`

, etc.) should live in the lowest universe, instead of being universe polymorphic.

Lastly, the kernel contains inductive types. The syntax to declare them is very similar to Coq’s. For example, you can write

inductive my_nat := | zero : my_nat | succ : my_nat → my_nat

which defines the natural numbers. This gives the type `my_nat`

with two constructors `my_nat.zero`

and `my_nat.succ`

, a dependent recursor `my_nat.rec`

and two computation rules.

Lean also automatically defines a couple of other useful definitions, like the injectivity of the constructors, but this is done outside the kernel (for a full list you can execute `print prefix my_nat`

if you’ve installed Lean). Lean supports inductive families and mutually defined inductive types, but not induction-induction or induction-recursion.

There is special support for *structures* – inductive types with only one constructor. For these the projections are automatically generated, and we can extend structures with additional fields. This is also implemented outside the kernel. In the following example we extend the structure of groups to the structure of abelian groups.

import algebra.group open algebra structure my_abelian_group (A : Type) extends group A := (mul_comm : ∀a b, mul a b = mul b a) print my_abelian_group check @my_abelian_group.to_group -- abelian_group.to_group : Π {A : Type}, abelian_group A → group A

The result is a structure with all the fields of `group`

, but with one additional field. Also, the coercion from `abelian_group`

to `group`

is automatically defined.

The Lean kernel can be instantiated in multiple ways. There is a *standard mode* where the lowest universe `Prop`

is impredicative and has proof irrelevance. The standard mode also has built-in quotient types. Secondly, there is the *HoTT mode* without impredicative or proof irrelevant universes. The HoTT mode also has some support for HITs; see below.

**Elaboration**

On top of the kernel there is a powerful elaboration engine which

- Infers implicit universe variables
- Infers implicit arguments, using higher order unification
- Supports overloaded notation or declarations
- Inserts coercions
- Infers implicit arguments using type classes
- Convert readable proofs to proof terms
- Constructs terms using tactics

It does most of these things simultaneously, for example it can use the term constructed by type classes to find out implicit arguments for functions.

Let’s run through the above list in more detail.

**1.** As said before, universe variables are rarely needed explicitly, usually only in cases where it would be ambiguous when you don’t give them (for example `sum.code`

as defined above could also live in `Type.{(max u v)+3}`

if the universe levels were not given explicitly).

**2.** As in Coq and Agda, we can use binders with curly braces `{...}`

to indicate which arguments are left implicit. For example with the identity function above, if we write `id (3 + 4)`

this will be interpreted as `@id _ (3 + 4)`

. Then the placeholder `_`

will be filled by the elaborator to get `@id ℕ (3 + 4)`

. Lean also supports *higher order unification*. This allows, for example, to leave the type family of a transport implicit. For example (`▸`

denotes transport):

open eq variables (A : Type) (R : A → A → Type) variables (a b c : A) (f : A → A → A) example (r : R (f a a) (f a a)) (p : a = b) : R (f a b) (f b a) := p ▸ r

Or the following lemma about transport in sigma-types:

open sigma sigma.ops eq variables {A : Type} {B : A → Type} {C : Πa, B a → Type} {a₁ a₂ : A} definition my_sigma_transport (p : a₁ = a₂) (x : Σ(b : B a₁), C a₁ b) : p ▸ x = ⟨p ▸ x.1, p ▸D x.2⟩ := eq.rec (sigma.rec (λb c, idp) x) p set_option pp.notation false check @my_sigma_transport -- my_sigma_transport : -- Π {A : Type} {B : A → Type} {C : Π (a : A), B a → Type} {a₁ a₂ : A} (p : eq a₁ a₂) (x : sigma (C a₁)), -- eq (transport (λ (a : A), sigma (C a)) p x) (dpair (transport B p (pr₁ x)) (transportD C p (pr₁ x) (pr₂ x)))

Here the first transport is along the type family `λa, Σ(b : B a), C a b`

. The second transport is along the type family `B`

and the `p ▸D`

is a dependent transport which is a map `C a₁ b → C a₂ (p ▸ b)`

. The `check`

command shows these type families. Also in the proof we don’t have to give the type family of the inductions `eq.rec`

and `sigma.rec`

explicitly. However, for nested inductions higher-order unification becomes expensive quickly, and it is better to use pattern matching or the `induction`

tactic, discussed below.

**3&4** Lean supports overloading and coercions. Some examples:

open eq is_equiv equiv equiv.ops example {A B : Type} (f : A ≃ B) {a : A} {b : B} (p : f⁻¹ b = a) : f a = b := (eq_of_inv_eq p)⁻¹

In this example `f`

is in the type of equivalences between `A`

and `B`

and is coerced into the function type. Also, `⁻¹`

is overloaded to mean both function inverse and path inverse.

**5.** Lean has type classes, similar to Coq’s and Agda’s, but in Lean they are very tightly integrated into the elaboration process. We use type class inference for inverses of functions. We can write `f⁻¹`

for an equivalence `f : A → B`

, and then Lean will automatically try to find an instance of type `is_equiv f`

.

Here’s an example, where we prove that the type of natural transformations between two functors is a set.

import algebra.category.nat_trans open category functor is_trunc example {C D : Precategory} (F G : C ⇒ D) : is_hset (nat_trans F G) := is_trunc_equiv_closed 0 !nat_trans.sigma_char

In the proof we only have to show that the type of natural transformation is equivalent to a sigma-type (`nat_trans.sigma_char`

) and that truncatedness respects equivalences (`is_trunc_equiv_closed`

). The fact that the sigma-type is a set is then inferred by type class resolution.

The brackets `[...]`

specify that that argument is inferred by type class inference.

**6.** There are multiple ways to write readable proofs in Lean, which are then converted to proof terms by the elaborator. One thing you can do is define functions using pattern matching. We already saw an example above with `sum.code`

. In contrast to Coq and Agda, these expressions are not part of the kernel. Instead, they are ‘compiled down’ to basic recursors, keeping Lean’s kernel simple, safe, and, well, ‘lean.’ Here is a simple example. The `print`

command shows how `inv`

is defined internally.

open eq definition my_inv {A : Type} : Π{a b : A}, a = b → b = a | my_inv (idpath a) := idpath a print my_inv -- definition my_inv : Π {A : Type} {a b : A}, a = b → b = a := -- λ (A : Type) {a b : A} (a_1 : a = b), eq.cases_on a_1 (idpath a)

Here are some neat examples with `vectors`

.

open nat inductive vector (A : Type) : ℕ → Type := | nil {} : vector A zero | cons : Π {n}, A → vector A n → vector A (succ n) open vector -- some notation. The second line allows us to use -- [1, 3, 10] as notation for vectors notation a :: b := cons a b notation `[` l:(foldr `,` (h t, cons h t) nil `]`) := l variables {A B : Type} definition map (f : A → B) : Π {n : ℕ}, vector A n → vector B n | map [] := [] | map (a::v) := f a :: map v definition tail : Π {n : ℕ}, vector A (succ n) → vector A n | n (a::v) := v -- no need to specify "tail nil", because that case is -- excluded because of the type of tail definition diag : Π{n : ℕ}, vector (vector A n) n → vector A n | diag nil := nil | diag ((a :: v) :: M) := a :: diag (map tail M) -- no need to specify "diag (nil :: M)" eval diag [[(1 : ℕ), 2, 3], [4, 5, 6], [7, 8, 9]] -- we need to specify that these are natural numbers -- [1,5,9]

You can use calculations in proofs, for example in the following construction of the composition of two natural transformations:

import algebra.category open category functor nat_trans variables {C D : Precategory} {F G H : C ⇒ D} definition nt.compose (η : G ⟹ H) (θ : F ⟹ G) : F ⟹ H := nat_trans.mk (λ a, η a ∘ θ a) (λ a b f, calc H f ∘ (η a ∘ θ a) = (H f ∘ η a) ∘ θ a : by rewrite assoc ... = (η b ∘ G f) ∘ θ a : by rewrite naturality ... = η b ∘ (G f ∘ θ a) : by rewrite assoc ... = η b ∘ (θ b ∘ F f) : by rewrite naturality ... = (η b ∘ θ b) ∘ F f : by rewrite assoc)

There are various notations for using forward reasoning. For example the following proof is from the standard library. In the proof below we use the notation ``p > 0``

, which is interpreted as the inhabitant of the type `p > 0`

in the current context. We also use the keyword `this`

, which is the name term constructed by the previous unnamed `have`

expression.

definition infinite_primes (n : nat) : {p | p ≥ n ∧ prime p} := let m := fact (n + 1) in have m ≥ 1, from le_of_lt_succ (succ_lt_succ (fact_pos _)), have m + 1 ≥ 2, from succ_le_succ this, obtain p `prime p` `p ∣ m + 1`, from sub_prime_and_dvd this, have p ≥ 2, from ge_two_of_prime `prime p`, have p > 0, from lt_of_succ_lt (lt_of_succ_le `p ≥ 2`), have p ≥ n, from by_contradiction (suppose ¬ p ≥ n, have p < n, from lt_of_not_ge this, have p ≤ n + 1, from le_of_lt (lt.step this), have p ∣ m, from dvd_fact `p > 0` this, have p ∣ 1, from dvd_of_dvd_add_right (!add.comm ▸ `p ∣ m + 1`) this, have p ≤ 1, from le_of_dvd zero_lt_one this, absurd (le.trans `2 ≤ p` `p ≤ 1`) dec_trivial), subtype.tag p (and.intro this `prime p`)

**7.** Lean has a tactic language, just as Coq. You can write

begin ...tactics... end

anywhere in a term to synthesize that subterm with tactics. You can also use `by *tactic*`

to apply a single tactic (which we used in the above calculation proof). The type of the subterm you want to synthesize is the *goal* and you can use tactics to solve the goal or apply backwards reasoning on the goal. For example if the goal is `f x = f y`

you can use the tactic ` apply ap f`

to reduce the goal to `x = y`

. Another tactic is the `exact`

tactic, which means you give the term explicitly.

The proof language offers various mechanisms to pass back and forth between the two modes. You can begin using tactics anywhere a proof term is expected, and, conversely, you can enter structured proof terms while in tactic mode using the `exact`

tactic.

A very simple tactic proof is

open eq variables {A B C : Type} {a a' : A} example (g : B → C) (f : A → B) (p : a = a') : g (f a) = g (f a') := begin apply ap g, apply ap f, exact p end

which produces the proof term `ap g (ap f p)`

. In the Emacs mode of Lean you can see the intermediate goals by moving your cursor to the desired location and pressing `Ctrl-C Ctrl-G`

Lean has too many tactics to discuss here, although not as many as Coq. Here are some neat examples of tactics in Lean.

The `cases`

tactic can be used to destruct a term of an inductive type. In the following examples, it destructs the path p to reflexivity. In the second example it uses that `succ`

is injective, since it is the constructor of `nat`

, so that it can still destruct `p`

. It also doesn’t matter whether the free variable is on the left hand side or the right hand side of the equality. In the last example it uses that different constructors of an inductive type cannot be equal.

open nat eq example {A : Type} {x y : A} (p : x = y) : idp ⬝ p = p := begin cases p, reflexivity end example (n m l : ℕ) (p : succ n = succ (m + l)) : n = m + l := begin cases p; reflexivity end example (n : ℕ) (p : succ n = 0) : empty := by cases p

The `rewrite`

tactic is useful for doing a lot of rewrite rules. It is modeled after the rewrite tactic in SSReflect. For example in the following proof of Eckmann-Hilton we rewrite the goal 4 times with theorems to solve the goal. The notation `▸*`

simplifies the goal similar to Coq’s `simpl`

, and `-H`

means to rewrite using `H⁻¹`

.

open eq theorem eckmann_hilton {A : Type} {a : A} (p q : idp = idp :> a = a) : p ⬝ q = q ⬝ p := begin rewrite [-whisker_right_idp p, -whisker_left_idp q, ▸*, idp_con, whisker_right_con_whisker_left p q] end

The `induction`

tactic performs induction. For example if the goal is `P n`

for a natural number `n`

(which need not be a variable), then you can use `induction n`

to obtain the two goals `P 0`

and `P (k + 1)`

assuming `P k`

. What’s really neat is that it supports user-defined recursors. So you can define the recursor of a HIT, tag it with the `[recursor]`

attribute, and then it can be used for the `induction`

tactic. Even more: you can arrange it in such a way that it uses the nondependent recursion principle whenever possible, and otherwise the dependent recursion principle.

import homotopy.circle types.int open circle equiv int eq pi definition circle_code (x : S¹) : Type₀ := begin induction x, -- uses the nondependent recursor { exact ℤ}, { apply ua, exact equiv_succ} -- equiv_succ : ℤ ≃ ℤ with underlying function succ end definition transport_circle_code_loop (a : ℤ) : transport circle_code loop a = succ a := ap10 !elim_type_loop a -- ! is the "dual" of @, i.e. it inserts -- placeholders for explicit arguments definition circle_decode {x : S¹} : circle_code x → base = x := begin induction x, -- uses the dependent recursor { exact power loop}, { apply arrow_pathover_left, intro b, apply concato_eq, apply pathover_eq_r, rewrite [power_con,transport_circle_code_loop]} end

In the future there will be more tactics, providing powerful automation such as a simplifier and a tableaux theorem prover.

I’ve been extensively working this year on the HoTT library of Lean, with the help of Jakob von Raumer and Ulrik Buchholtz. The HoTT library is coming along nicely. We have most things from the first 7 chapters of the HoTT book, and some category theory. In this file there is a summary what is in the HoTT library sorted by where it appears in the HoTT book.

I’ve developed the library partly by porting it from the Coq-HoTT library, since Coq’s syntax is pretty close to Lean. Other things I’ve proven using the proof in the book, or just by proving it myself.

In the library we are heavily using the cubical ideas Dan Licata wrote about earlier this year. For example we have the type of pathovers, which are heterogenous paths lying over another path. These can be written as `b =[p] b'`

(or `pathover B b p b'`

if you want to give the fibration explicitly). These are used for the recursion principle of HITs

definition circle.rec {P : circle → Type} (Pbase : P base) (Ploop : Pbase =[loop] Pbase) (x : circle) : P x

and for the equality in sigma’s

variables {A : Type} {B : A → Type} definition sigma_eq_equiv (u v : Σa, B a) : (u = v) ≃ (Σ(p : u.1 = v.1), u.2 =[p] v.2)

There are also squares and squareovers which are presentations of higher equalities. One neat example which shows the strength of these additional structures is the following example. I had the term and could fill every square in the diagram below. I wanted to rewrite this term to .

If every square is formulated as a path between compositions of paths, then this rewriting takes a lot of work. You have to rewrite any individual square (assuming it is formulated as “top equals composition of the other three sides”), then you have to perform a lot of associativity steps to be able to cancel the sides which you get in the middle of the diagram.

Or you can formulate and prove a dedicated lemma for this rewrite step, which also takes quite some work.

However, because I formulated these two-dimensional paths as squares, I could first horizontally compose the four squares. This gives a square with as top and as bottom . Then you can apply the theorem that the top of a square equals the composition of the other 3 sides, which gives exactly the desired rewrite rule. Similar simplifications by the use of pathovers and squares are occurring all over the library.

**HITs**

Lastly I want to talk about our way of handling HITs in Lean. In Coq and Agda the way to define a HIT is to make a type with the point constructors and then (inconsistently) assume that this type contains the right path constructors and the correct induction principle and computation rules. Then we forget we assumed something which was inconsistent. This is often called Dan Licata’s trick. This works well, but it’s not so clean since it assumes an inconsistency.

In Lean, we add two specific HITs as kernel extension. These HITs are the quotient and the -truncation. The formation rule, the constructors and the recursion principle are added as constants, and the computation rule on the points is added as a definitional equality to the kernel (the computation rule for paths is an axiom).

The quotient (not to be confused with a set-quotient) is the following HIT: (this is not Lean syntax)

HIT quotient {A : Type} (R : A → A → Type) := | i : A → quotient R | p : Π{a a' : A}, R a a' → i a = i a'

So in a quotient we specify the type of points, and we can add any *type* of paths to the quotient.

In Lean we have the following constants for quotients. The computation rule for points, `rec_class_of`

, is just defined as reflexivity, to illustrate that the reduction rule is added to the Lean kernel.

open quotient print quotient print class_of print eq_of_rel print quotient.rec print rec_class_of print rec_eq_of_rel

Using these HITs, we can define all HITs in Chapters 1-7 of the book. For example we can define the sequential colimit of a type family with functions by taking the quotient of the type with relation defined as an inductive family

inductive R : (Σn, A n) → (Σn, A n) → Type := | Rmk : Π{n : ℕ} (a : A n), R ⟨n+1, f a⟩ ⟨n, a⟩

In similar ways we can define pushouts, suspensions, spheres and so on. But you can define more HITs with quotients. In my previous blog post I wrote how to construct the propositional truncation using just quotients. In fact, Egbert Rijke and I are working on a generalization of this construction to construct all -truncations, which means we can even drop -truncations as a primitive HIT.

Quotients can even be used to construct HITs with 2-path constructors. I have formalized a way to construct quite general HITs with 2-constructors, as long as they are nonrecursive. The HIT I constructed has three constructors. The point constructor `i`

and path constructor `p`

are the same as for the quotient, but there is also a 2-path constructor, which can equate

- One path constructors
- Reflexivity
`ap i p`

where`i`

is the point constructor and`p`

is a path in`A`

- concatenations and/or inverses of such paths

Formally, the defintion of the HIT is as follows. Given a type and a binary (type-valued) relation on . Let be the “formal equivalence closure” of , i.e. the following inductive family:

inductive T : A → A → Type := | of_rel : Π{a a'}, R a a' → T a a' | of_path : Π{a a'}, a = a' → T a a' | symm : Π{a a'}, T a a' → T a' a | trans : Π{a a' a''}, T a a' → T a' a'' → T a a''

Note that if we’re given a map we can extend it to a map by interpreting e.g. .

Now if you’re also given a “relation on ”, i.e. a family , then we can construct the following type just using quotients

HIT two_quotient A R Q, i : A → two_quotient A R Q p : Π{a a' : A}, R a a' → i a = i a' r : Π{a a'} (r s : T a a'), Q r s → p* r = p* s

This `two_quotient`

allows to construct at least the following HITs:

- The torus (the formulation with two loops and a 2-constructor)
- The reduced suspension
- The groupoid quotient

So in conclusion, from quotients you can define the most commonly used HITs. You can (probably) not define every possible HIT, though, so for the other HITs you still have to use Dan Licata’s trick. I should note here that currently there is no `private`

modifier in Lean yet, so we cannot do Dan Licata’s trick very well in Lean now.

These are all the topics I want to talk about in this blog post. I tried to cover as much of the functionalities of Lean, but there are still features I haven’t talked about. For more information, you can look at the Lean tutorial. Keep in mind that the tutorial is written for the standard library, not the HoTT library, and you might want to skip the first couple of chapters explaining dependent type theory. The Quick Reference chapter at the end of the tutorial (or in pdf format) is also very useful. Also feel free to ask any questions in the comments, in the lean-user google group or in an issue on Github. Finally, if you’re willing to help with the HoTT library, that would be very much appreciated!

]]>

- Brouwer’s fixed-point theorem in real-cohesive homotopy type theory by me, and
- Adjoint logic with a 2-category of modes, by Dan Licata with a bit of help from me.

Both of them have fairly chatty introductions, so I’ll try to restrain myself from pontificating at length here about their contents. Just go read the introductions. Instead I’ll say a few words about how these papers came about and how they are related to each other.

Cohesive homotopy type theory is something that I’ve been working on with Urs Schreiber since some 3-4 years ago, involving an adjoint string of modalities ʃ that internalizes Lawvere’s axiomatic cohesion. We wrote a little note about it. About a year ago, I started to wonder whether cohesive HoTT could solve “the problem of the two circles”. By this I mean the fact that in HoTT we have both the *higher inductive* circle, generated by a point `base` and an equality `loop : base = base`, and the *topological* circle, defined for instance as . The former is a 1-type that has “only one point” (to be precise, it is 0-connected), whereas the latter is a 0-type (a set) that has infinitely many distinct points. The “problem” is, how are these two circles related?

In March I gave a talk about this at CMU, in which I introduced an enhancement of cohesive HoTT called “real-cohesive HoTT” and proved in it that the “shape” ʃ (the cohesive incarnation of the fundamental -groupoid) of the topological circle is the homotopical circle. At that point Dan Licata, who was in the audience, got interested and started convincing me to look for a better way to formulate cohesion type-theoretically.

The problem with cohesive type theory is that the comodality , unlike the modalities ʃ and , *can’t* be described internally as a map with properties. Urs and I first noticed this semantically; later I proved a no-go theorem internally (see section 4 of the real-cohesion paper). Urs and I worked around this by representing as a map ; while later, I started experimenting with using Coq’s modules to restrict the context. But in March Dan told me about *modal type theories* due to Pfenning-Davies and Reed, in which a segregation of the context limits the applicability of modal type formers. The existing work had focused on more traditional modalities such as necessity and possibility , but Dan thought there ought to be a version that would work for the cohesive modalities as well.

There followed a very productive back-and-forth in which Dan developed the metatheory with an eye towards a proof-theoretically well-behaved calculus, while I cast around for a version of the theory that would make for a perspicuous presentation of real-cohesion. We ended up with an idea for a very general kind of “adjoint type theory”, which is not yet worked out in full; the real-cohesion paper uses only one particular case of it, while the adjoint logic paper develops the metatheory of the general case but in a very simplified way (no dependent types and only one-variable contexts).

Dan coded up a simple sequent calculus for this theory in Agda and proved some nice theorems about it. Meanwhile I generalized it to dependent type theory in the special case of cohesion and used it to write up the two circles theorem, and also a synthetic version of the classical homotopy-theoretic proof of Brouwer’s fixed-point theorem as a proof of concept. I’m very excited about real-cohesion, and also adjoint type theory more generally; I think it has a lot of untapped potential. A few possibilities are mentioned in the paper.

Unfortunately there is one fly in the ointment: cohesive type theory done in this way can’t be formalized directly in existing proof assistants. One can describe the theory using a proof assistant as the metatheory, of course. For actually proving things *in* the theory, this works pretty well for basic theorems in a 1-variable sequent calculus (see the Agda code accompanying the adjoint logic paper), but for more complicated results in a richer language it would become quite tedious, running into the common problems with representing languages with variable binding and dependent types. At one point I entertained notions that Coq’s modules would help; but at the moment I’m kind of down on that idea because modules have a lot of “features” that make them at best annoying to use in this way (which, to be fair, is not at all how they were intended to be used). So formalization may have to wait until someone implements an adjoint-type-theory proof assistant directly. (Anyone developing a proof assistant feel like incorporating it?)

]]>

I’ve just posted a preprint which improves that state a bit, providing a version of “Lang(*C*)” containing univalent strict universes for a wider class of (∞,1)-toposes *C*:

Previously our collection of such models included the following two examples:

- Slice (∞,1)-categories of ∞Gpd, obtained from slices of the model category of simplicial sets. Here the universes can be obtained simply by pullback to the appropriate slice. (In type theory, this just means working in a nonempty context.) Just as in ordinary category theory, in higher category theory we have , so these give us models in (∞,1)-toposes of diagrams on any ∞-groupoid.
- Diagrams of ∞-groupoids indexed by inverse 1-categories. These are a sort of “well-founded diagram” which enables us to build universes inductively step-by-step.

The idea of this paper is that we should be able to combine these models. Essentially, we build universes inductively as in an inverse diagrams model, but each step of the induction is a slice category. This results in diagrams indexed by something I’m calling “inverse EI (∞,1)-categories”, which are like inverse categories except that they can contain nontrivial automorphisms (and higher automorphisms). The terminology comes from the classical notion of “EI category”, which is one in which all Endomorphisms are Isomorphisms; these are a generalization to the ∞-case but with a well-foundedness restriction.

I had this thought over a year ago, and with the idea in hand, working out the details was fairly straightforward. There are several reasons it took me so long. One is named Arthur and just turned two years old last month. Another is that along the way I got caught up in a spin-off project. But the most important is that I got stuck — not in constructing the new models of type theory, but in proving that they model the desired (∞,1)-categories!

There are by now a *lot* of models for (∞,1)-categories and diagrams on them, but it turns out that the one I needed didn’t quite exist yet. The indexing object that most naturally falls out of this approach is an *internal* category in simplicial sets, and until last year these had not been shown to admit a model structure presenting the theory of (∞,1)-categories. In fact, I myself had wondered about that for quite unrelated reasons; and last March Geoffroy Horel proved it. It turns out to be quite subtle! The basic idea (non-model-category-enthusiasts can skip to the next paragraph now) is to lift the complete-Segal-space model structure along the nerve functor to bisimplicial sets; but instead of Rezk’s complete-Segal-space model structure that is a localization of the Reedy (i.e. injective) model structure, Horel has to use an analogous one that localizes the *projective* model structure on bisimplicial sets.

Horel had also constructed a model structure on the category of internal presheaves over an internal category in simplicial sets, which it was easy to compare mine to. But what remained was to connect this model structure to some previously-known model category for (∞,1)-presheaves, to show that it has the right homotopy theory. Geoffroy and I went back and forth with several ideas, but nothing seemed to work easily. Finally, a couple months ago I learned that Pedro Boavida de Brito had constructed a “Grothendieck construction” relating internal presheaves on internal categories to an appropriate model structure of “left fibrations” over complete Segal spaces (the latter I had also heard of several years earlier from Charles Rezk, but has still never been published). He and I were then able to work out how to relate the latter to left fibrations over quasicategories or to enriched diagrams on simplicially enriched categories. His paper is not yet available, but he gave me permission to cite it forwards in my preprint.

There are a few reasons I think this result is interesting. One is, of course, that we still don’t know whether all (∞,1)-toposes model type theory with univalent strict universes, so any progress in this direction is nice. Another is that classical *equivariant algebraic topology* is in fact equivalent to the homotopy theory of diagrams on some inverse EI (∞,1)-category — as long as the group of equivariance is *compact Lie*! (I always wondered, when hearing about equivariant homotopy theory as a graduate student, why compact Lie groups seemed to make certain things better-behaved. These models may or may not have anything to do with any of the advantages of compact Lie groups that equivariant homotopy theorists are familiar with, but it’s intriguing that the same condition arises.) Thus, synthetic homotopy theory can be used (modulo the expected initiality theorem) to prove things about equivariant homotopy theory, which is a notoriously difficult subject.

Finally, I believe this is the first model for univalence involving a model category that is cooked up expressly for the purpose. Voevodsky’s original model used Quillen’s model category of simplicial sets, and my earlier models in inverse diagrams and elegant Reedy presheaves used the Reedy model structure due to Reedy and Kan. But the model categories that I ended up with in this paper seem to be new; they have a Reedy flavor (thinking about this led to the spin-off work I mentioned above) but I have not seen them anywhere else. (In higher category theory language, they are an intriguing mixture of “algebraic” and “non-algebraic”: the automorphisms act non-algebraically, while the noninvertible maps act algebraically.) This gives me some hope that the general problem may be solvable: so far we haven’t managed to build univalent universes in existing model categories such as injective simplicial presheaves, but maybe somewhere out there there are different model categories that are better-adapted to modeling type theory.

Unfortunately, I don’t see any useful way of directly generalizing the construction in this paper; it still seems very closely tied to the well-founded-induction inverse-category philosophy. I guess there is some hope that it might generalize to a sort of “elegant generalized-Reedy (∞,1)-category”, but so far I have not managed to make anything like that work.

]]>

The construction of the propositional truncation of a type is as follows. Let be the following HIT:

HIT {-} (A : Type) : Type := | f : A → {A} | e : ∀(a b : A), f a = f b

I call this the “*one step truncation*”, because the HIT is like the propositional truncation, but takes out the recursive part. Martin Escardo calls this the *generalized circle*, since . We can repeat this multiple times: and . Now the colimit of this sequence is the propositional truncation of : . To make sure we’re on the same page, and to introduce the notation I use for the constructors, the (sequential) colimit is the following HIT: (I will mostly leave the arguments in implicit)

HIT colimit (A : ℕ → Type) (f : ∀(n : ℕ), A n → A (n+1)) : Type := | i : ∀(n : ℕ), A n → colimit A f | g : ∀(n : ℕ) (a : A n), i (f a) = i a

Below I’ll give an intuition of why the construction works, and after that a proof. But first I want to talk about some consequences of the construction.

First of all this is the first instance of a HIT which has a recursive path constructor which has been reduced to non-recursive HITs. During the HoTT workshop in Warsaw there was a conjecture that all HITs with recursive (higher) path constructors can be reduced to HITs where only point constructors are allowed to be recursive. Although I have no idea whether this conjecture is true in general, this is the first step towards proving it.

Secondly, this gives an alternative answer to the question about the universal property of the propositional truncation. This construction shows that (using function extensionality) a function for an arbitrary type is “the same” as a sequence of functions (with defined above) such that for all we have . In a formula

.

**So, why does the construction work?**

To give an intuition why this works consider the propositional truncation of the booleans, . Of course, this type is equivalent to the interval (and any other contractible type), but there is actually a little more going on. The path constructor of the propositional truncation (let’s call it here) gives rise to two paths between and : and . Since the resulting type is a mere proposition, these paths must be equal. We can construct a path between them explicitly by using the continuity of . We can prove that for every and every we have by path induction, and this leads to the conclusion that any two elements in are equal. This is exactly the proof that any proposition is a set.

Now consider the type . This is a type with points and and we have two paths from to . Let’s call them and . These two paths are *not* equal. Of course, we also have a loop at both and .

However, in the type the corresponding paths and of type *are* equal. To see this, we can mimic the above proof. We can prove that for all and all we have , just by path induction on . Then we can conclude that . Of course, in there are *new* paths of type , but they will be identified again in .

<sidenote>

The above argument also shows that if any function is weakly constant, then is weakly constant for any (using the terminology of this post).

</sidenote>

So this is the intuition why is the propositional truncation of . In a path is added between any two points of , between any two *existing* parallel paths (and between any two parallel higher paths). Then in higher paths are added between all newly introduced (higher) paths, and so on. In the colimit, paths exist between any two points, parallel paths, and parallel higher paths, so we get the propositional truncation.

**The proof.**

But this is just the intuition. How do we prove that we get the propositional truncation? We clearly get the point constructor for the propositional truncation, because (the constructor of the colimit) has type . Now we still have to show two things:

- We have to show that is a mere proposition
- We have to construct the induction principle for the propositional truncation on , with the judgmental computation rule

**2.** The second part is easy. If we’re given a family of propositions with a map we have to construct a map . We do this by induction on . Since we construct something in , which is a mere proposition, the path construction is automatic, and we only have to deal with the case that for some and . Now we do induction on .

If , we can choose the element .

If , we know that , so we can induct on . If , we need an element of . We have an element of by induction hypothesis. So we can transport this point along the equality to get the desired point. The path construction is again automatic. In pattern matching notation we have defined:

- and
- .

The definition is also the (judgmental) computation rule for the propositional truncation.

**1.** So we only need to show that is a mere proposition, i.e. . We first do induction on . Note that is a mere proposition. (Left as exercise to the reader. Hint: assume that it is inhabited.) So we only have to consider the case where is a point constructor, say . Now we do induction on . Now the goal is , and we don’t know (yet) that this is a mere proposition, so we have to show two things:

- We have to construct a for all ;
- We have to show that satisfies the following coherence condition: .

In particular, we want to make the construction of as simple as possible, so that the coherence condition for remains doable.

For the construction of , let’s first consider a special case where and live in the same level, i.e. where . This will be used in the general construction. In this case, we have , where the second equality is by the path constructor of and the other two equalities are by the path constructor of the colimit. Let’s call this equality .

For the general construction of with arbitrary and , I have considered three approaches. I will first discuss two approaches where I failed to complete the proof.

(failed) **Approach 1.** Induct on both and . In the successor case, induct on and . Unfortunately, this becomes horrible very quickly, because we now have a nested double induction on HITs. This means that we already need to prove coherence conditions just to construct , and after that we still need to show that satisfies a coherence condition. I quickly gave up on this idea.

(failed) **Approach 2.** Lift and both to by repeatedly applying , and then show that , where the second equality is . This works, but there is a catch: lives in and lives in (assuming addition is defined by induction on the second argument). So to complete the construction, we also have to transport along the equality . This is fine, but for the coherence condition for this transport becomes an annoying obstacle. Showing the coherence condition is possible, but I didn’t see how to do it. Then realized there was a more convenient approach, where I didn’t have to worry about transports.

(succeeded) **Approach 3.** Distinguish cases between and and *induct on those inequalities*. I’m assuming that the inequality is defined by an (ordinary) inductive family

inductive le (n : ℕ) : ℕ → Type := | refl : le n n | step : Π(m : ℕ), le n m → le n (m+1)

This definition gives a very useful induction principle for this construction. If we can construct a map by induction on :

- ;
- .

I use the notation for this, similar to , because we repeatedly apply and the number of times is determined by . Since the type is a mere proposition, doesn’t really depend on , only on the fact that its type has an inhabitant.

We can now also show that by induction on as concatenation of multiple s. Now if we can construct by , where the first equality is . The case is similar.

This is a nice and simple construction of (without transports), and we can prove the coherence conditions using this definition, but that is more involved.

We have constructed in such a way that for we have that equals and in the case that we have that equals . This is not purely by definition, for the case we have to prove something here, because we cannot get both inequalities by definition.

Now we have to show that . We distinguish two cases: and .

**Case **

In this case we can find and . In this case and

The situation is displayed below (click image for larger view). Some arguments of paths are omitted, and the maps and are also omitted for readability (the actual equalities live in ). We need to fill the pentagon formed by the outer (thick) equalities.

By induction on we can find a path and by another induction on we can fill the bottom square in the diagram. We will skip the details here. The top triangle can be filled for a general path by induction on . This finishes the case .

**Case **

Now we can find and . Here and . The situation is below.

If we choose correctly (namely as ), then by definition and , so the triangle in the left of the diagram is a definitional equality. We can prove the remaining square in more generality. See the below diagram, where we mention explicitly.

The bottom square is filled by induction on . To show that the two paths in the top are equal, first note that is weakly constant, because that is exactly the type of . So is weakly constant. This finishes the case , and hence the proof that is a mere proposition.

**Future Work**

The natural question is whether we can construct arbitrary -truncations using non-recursive HITs. The construction in this post can be easily generalized by using an “one-step -truncation,” but the proof that the resulting type is -truncated cannot be generalized easily. However, Egbert Rijke has a proof sketch of the construction of localizations between -compact types using nonrecursive HITs, and these include all the -truncations.

]]>

The topic of my talk can be summarised as

Question: What is the type ?

This question is very easy if is propositional, because then we have

Partial answer (1): If is propositional, then

by the usual universal property, with the equivalence given by the canonical map. Without an assumption on , it is much harder.

The rest of this post contains a special case which already contains some of the important ideas. It is a bit lengthy, and in case you don’t have time to read everything, here is the

Main result (general universal property of the propositional truncation).

In a dependent type theory with at least , -, -, and identity types, which furthermore has Reedy -limits (“infinite -types”), we can define thetype of coherently constant functionsas the type of natural transformations between type-valued presheaves. If the type theory has propositional truncations, we can construct a canonical map from to . If the type theory further has function extensionality, then this canonical map is an equivalence.

If is known to be -truncated for some fixed , we can drop the assumption of Reedy -limits and perform the whole construction in “standard syntactical” HoTT. This describes how functions can be defined if is not known to be propositional, and it streamlines the usual approach of finding a propositional with and .

**Here comes the long version of this blog post.** So, what is a function ? If we think of elements of as *anonymous* inhabitants of , we could expect that such a is “the same” as a function which “cannot look at its input”. But then, how can we specify what it means to “not look at its input” internally? A first attempt could be requesting that is *weakly constant*, . Indeed, it has been shown:

Partial answer (2): If is a set (h-set, -truncated), then , where the function from left to right is the canonical one.

It is not surprising that we still need this strong condition on if we want weak constancy to be a sufficient answer: just throwing in a bunch of paths, which might or might not “fit together” (we just don’t know anything) seems wrong. Indeed, from Mike’s recent construction, we know that the statement does become false (contradicts univalence) without this requirement.

Given a function and a proof , we can try to fix the problem that the paths given by “do not fit together” by throwing in a coherence proof, i.e. an element of . We should already know that this will introduce its own problems and thus not fix everything, but at least, we get:

Partial answer (3): If is -truncated, then , again given by a canonical map from left to right.

In my talk, I have given a proof of “partial answer (3)” via “reasoning with equivalences” (slides p. 5 ff). I start by assuming a point . The type is then equivalent to the following (explanation below):

In the above long nested -type, the first line is just the that we start with. The second line adds two factors/-components which, by function extensionality, cancel each other out (they form a singleton). The same is true for the pair in the third line, and for the pair in the fourth line. Lines five and six look different, but they are not really; it’s just that their “partners” (which would complete them to singletons) are hard to write down and, at the same time, contractible; thus, they are omitted. As is assumed to be a -type, it is easy to see that the -components in lines five and six are both propositional, and it’s also easy to see that the other -components imply that they are both inhabited. Of course, the seventh line does nothing.

Simply by re-ordering the -components in the above type, and not doing anything else, we get:

By the same argument as before, the components in the pair in line two cancel each other out (i.e. the pair is contractible). The same is true for line three. Line four and five are propositional as is -truncated, but easily seen to be inhabited. Thus, the whole nested -type is equivalent to .

In summary, we have constructed an equivalence . By going through the construction step-by-step, we see that the function part of the equivalence (map from left to right) is the canonical one (let’s write ), mapping to the triple . Before we have started the construction, we have assumed a point . But, and this is the crucial observation, the *function* does not depend on ! So, if the assumption implies that is an equivalence, then is already enough. Thus, we have . We can move the -part to both sides of the equivalence, and on the right-hand side, we can apply the usual “distributivity of and (or )”, to move the into each -component; but in each -component, the gets eaten by an (we have that and are equivalent), which gives us the claimed result.

The strategy we have used is very minimalistic. It does not need any “technology”: we just add and remove contractible pairs. For this “expanding and contracting” strategy, we have really only used very basic components of type theory (, , identity types with function extensionality, propositional truncation, but even that only in the very end). If we want to weaken the requirement on by one more level (i.e. if we want to derive a “universal property” which characterises if is -truncated), we have to add one more coherence condition (which ensures that the -component is well-behaved). The core idea is that this expanding and contracting strategy can be done for any truncation level of , even if is not known to be -truncated at all, where the tower of conditions becomes infinite. I call an element of this “infinite -type” a *coherently constant function* from to .

The idea is that the -components and and we used in the special case can be seen as components of a natural transformation between -truncated semi-simplicial types. By *semi-simplicial type*, I mean a Reedy fibrant diagram . By *-truncated semi-simplicial types*, I mean the “initial segments” of semi-simplicial types, the first three components as described here.

The first diagram we need is what I call the “trivial semi-simplicial type” over , written , and its initial part is given by , , and . If we use the “fibred” notation (i.e. give the fibres over the matching objects), this would be , , . In the terminology of simplicial sets, this is the *-coskeleton* of [the diagram that is constantly] .

The second important diagram, I call it the *equality semi-simplicial type* over , is written . One potential definition for the lowest levels would be given by (I only give the fibres this time): , , . This is a *fibrant replacement* of . (We are lucky because is already fibrant; otherwise, we should have constructed a fibrant replacement as well.)

If we now check what a (strict) natural transformation between and (viewed as diagrams over the full subcategory of with objects ) is, it is easy to see that the -component is exactly a map , the -component is exactly a proof , and the -component is just a proof . (The *type* of such natural transformations is given by the limit of the exponential of and .)

However, even with this idea, and with the above proof for the case that is -truncated, generalising this proof to the infinite case with infinitely many coherence conditions requires some ideas and a couple of technical steps which, for me, have been quite difficult. Therefore, it has taken me a very long time to write this up cleanly in an article. I am very grateful to the reviewers (this work is going to appear in the post-proceedings of TYPES’14) and to Steve (my thesis examiner) for many suggestions and interesting connections they have pointed out. In particular, one reviewer has remarked that the main result (the equivalence of coherently constant functions and maps out of the truncation ) is a type-theoretic version of Proposition 6.2.3.4 in Lurie’s Higher Topos Theory; and Vladimir has pointed out the connection to the work of his former student Alexander Vishik. Unfortunately, both are among the many things that I have yet to understand, but there is certainly a lot to explore. If you can see further connections which I have not mentioned here or in the paper, then this is very likely because I am not aware of them, and I’d be happy to hear about it!

]]>