1.5 Product types
Given types we introduce the type , which we call their cartesian product. We also introduce a nullary product type, called the unit type . We intend the elements of to be pairs , where and , and the only element of to be some particular object . However, unlike in set theory, where we define ordered pairs to be particular sets and then collect them all together into the cartesian product, in type theory, ordered pairs are a primitive concept, as are functions.
There is a general pattern for introduction of a new kind of type in type theory, and because products are our second example following this pattern,11The description of universes above is an exception. it is worth emphasizing the general form: To specify a type, we specify:
how to form new types of this kind, via formation rules. (For example, we can form the function type when is a type and when is a type. We can form the dependent function type when is a type and is a type for .)
how to construct elements of that type. These are called the type’s constructors or introduction rules. (For example, a function type has one constructor, -abstraction. Recall that a direct definition like can equivalently be phrased as a -abstraction .)
how to use elements of that type. These are called the type’s eliminators or elimination rules. (For example, the function type has one eliminator, namely function application.)
an optional uniqueness principle33also referred to as -expansion, which expresses uniqueness of maps into or out of that type. For some types, the uniqueness principle characterizes maps into the type, by stating that every element of the type is uniquely determined by the results of applying eliminators to it, and can be reconstructed from those results by applying a constructor—thus expressing how constructors act on eliminators, dually to the computation rule. (For example, for functions, the uniqueness principle says that any function is judgmentally equal to the “expanded” function , and thus is uniquely determined by its values.) For other types, the uniqueness principle says that every map (function) from that type is uniquely determined by some data. (An example is the coproduct type introduced in §1.7 (http://planetmath.org/17coproducttypes), whose uniqueness principle is mentioned in §2.15 (http://planetmath.org/215universalproperties).)
When the uniqueness principle is not taken as a rule of judgmental equality, it is often nevertheless provable as a propositional equality from the other rules for the type. In this case we call it a propositional uniqueness principle. (In later chapters we will also occasionally encounter propositional computation rules.)
The inference rules in §A.3 (http://planetmath.org/a3homotopytypetheory) are organized and named accordingly; see, for example, §A.2.4 (http://planetmath.org/a24dependentfunctiontypespitypes), where each possibility is realized.
The way to construct pairs is obvious: given and , we may form . Similarly, there is a unique way to construct elements of , namely we have . We expect that “every element of is a pair”, which is the uniqueness principle for products; we do not assert this as a rule of type theory, but we will prove it later on as a propositional equality.
Now, how can we use pairs, i.e. how can we define functions out of a product type? Let us first consider the definition of a non-dependent function . Since we intend the only elements of to be pairs, we expect to be able to define such a function by prescribing the result when is applied to a pair . We can prescribe these results by providing a function . Thus, we introduce a new rule (the elimination rule for products), which says that for any such , we can define a function by
We avoid writing here, in order to emphasize that is not a function on a product. (However, later on in the book we will often write both for functions on a product and for curried functions of two variables.) This defining equation is the computation rule for product types.
Note that in set theory, we would justify the above definition of by the fact that every element of is a pair, so that it suffices to define on pairs. By contrast, type theory reverses the situation: we assume that a function on is well-defined as soon as we specify its values on tuples, and from this (or more precisely, from its more general version for dependent functions, below) we will be able to prove that every element of is a pair. From a category-theoretic perspective, we can say that we define the product to be left adjoint to the “exponential” , which we have already introduced.
As an example, we can derive the projection functions
with the defining equations
Rather than invoking this principle of function definition every time we want to define a function, an alternative approach is to invoke it once, in a universal case, and then simply apply the resulting function in all other cases. That is, we may define a function of type
with the defining equation
Then instead of defining functions such as and directly by a defining equation, we could define
We refer to the function as the recursor for product types. The name “recursor” is a bit unfortunate here, since no recursion is taking place. It comes from the fact that product types are a degenerate example of a general framework for inductive types, and for types such as the natural numbers, the recursor will actually be recursive. We may also speak of the recursion principle for cartesian products, meaning the fact that we can define a function as above by giving its value on pairs.
We leave it as a simple exercise to show that the recursor can be derived from the projections and vice versa.
We also have a recursor for the unit type:
with the defining equation
Although we include it to maintain the pattern of type definitions, the recursor for is completely useless, because we could have defined such a function directly by simply ignoring the argument of type .
To be able to define dependent functions over the product type, we have to generalize the recursor. Given , we may define a function by providing a function with defining equation
For example, in this way we can prove the propositional uniqueness principle, which says that every element of is equal to a pair. Specifically, we can construct a function
Here we are using the identity type, which we are going to introduce below in §1.12 (http://planetmath.org/112identitytypes). However, all we need to know now is that there is a reflexivity element for any . Given this, we can define
This construction works, because in the case that we can calculate
using the defining equations for the projections. Therefore,
is well-typed, since both sides of the equality are judgmentally equal.
More generally, the ability to define dependent functions in this way means that to prove a property for all elements of a product, it is enough to prove it for its canonical elements, the tuples. When we come to inductive types such as the natural numbers, the analogous property will be the ability to write proofs by induction. Thus, if we do as we did above and apply this principle once in the universal case, we call the resulting function induction for product types: given we have
with the defining equation
Similarly, we may speak of a dependent function defined on pairs being obtained from the induction principle of the cartesian product. It is easy to see that the recursor is just the special case of induction in the case that the family is constant. Because induction describes how to use an element of the product type, induction is also called the (dependent) eliminator, and recursion the non-dependent eliminator.
Induction for the unit type turns out to be more useful than the recursor:
with the defining equation
Induction enables us to prove the propositional uniqueness principle for , which asserts that its only inhabitant is . That is, we can construct
by using the defining equations
or equivalently by using induction:
|Title||1.5 Product types|