| ## What makes dependent type theory dependent? | |
| The short explanation is that what makes dependent type theory dependent is that types can depend on parameters. | |
| You have already seen a nice example of this: the type ``List α`` depends on the argument ``α``, and | |
| this dependence is what distinguishes ``List Nat`` and ``List Bool``. | |
| For another example, consider the type ``Vector α n``, the type of vectors of elements of ``α`` of length ``n``. | |
| This type depends on *two* parameters: the type ``α : Type`` of the elements in the vector and the length ``n : Nat``. | |
| Suppose we wish to write a function ``cons`` which inserts a new element at the head of a list. | |
| What type should ``cons`` have? Such a function is *polymorphic*: we expect the ``cons`` function for ``Nat``, ``Bool``, | |
| or an arbitrary type ``α`` to behave the same way. | |
| So it makes sense to take the type to be the first argument to ``cons``, so that for any type, ``α``, ``cons α`` | |
| is the insertion function for lists of type ``α``. In other words, for every ``α``, ``cons α`` is the function that takes an element ``a : α`` | |
| and a list ``as : List α``, and returns a new list, so we have ``cons α a as : list α``. | |
| It is clear that ``cons α`` should have type ``α → List α → List α``. But what type should ``cons`` have? | |
| A first guess might be ``Type → α → list α → list α``, but, on reflection, this does not make sense: | |
| the ``α`` in this expression does not refer to anything, whereas it should refer to the argument of type ``Type``. | |
| In other words, *assuming* ``α : Type`` is the first argument to the function, the type of the next two elements are ``α`` and ``List α``. | |
| These types vary depending on the first argument, ``α``. | |
| This is an instance of a *dependent function type*, or *dependent arrow type*. Given ``α : Type`` and ``β : α → Type``, | |
| think of ``β`` as a family of types over ``α``, that is, a type ``β a`` for each ``a : α``. | |
| In that case, the type ``(a : α) → β a`` denotes the type of functions ``f`` with the property that, | |
| for each ``a : α``, ``f a`` is an element of ``β a``. In other words, the type of the value returned by ``f`` depends on its input. | |
| Notice that ``(a : α) → β`` makes sense for any expression ``β : Type``. When the value of ``β`` depends on ``a`` | |
| (as does, for example, the expression ``β a`` in the previous paragraph), ``(a : α) → β`` denotes a dependent function type. | |
| When ``β`` doesn't depend on ``a``, ``(a : α) → β`` is no different from the type ``α → β``. | |
| Indeed, in dependent type theory (and in Lean), ``α → β`` is just notation for ``(a : α) → β`` when ``β`` does not depend on ``a``. | |
| Returning to the example of lists, we can use the command `#check` to inspect the type of the following `List` functions | |
| We will explain the ``@`` symbol and the difference between the round and curly braces momentarily. | |
| ```lean | |
| #check @List.cons -- {α : Type u_1} → α → List α → List α | |
| #check @List.nil -- {α : Type u_1} → List α | |
| #check @List.length -- {α : Type u_1} → List α → Nat | |
| #check @List.append -- {α : Type u_1} → List α → List α → List α | |
| ``` | |
| Just as dependent function types ``(a : α) → β a`` generalize the notion of a function type ``α → β`` by allowing ``β`` to depend on ``α``, | |
| dependent Cartesian product types ``(a : α) × β a`` generalize the Cartesian product ``α × β`` in the same way. Dependent products are also | |
| called *sigma* types, and you can also write them as `Σ a : α, β a`. You can use `⟨a, b⟩` or `Sigma.mk a b` to create a dependent pair. | |
| ```lean | |
| universe u v | |
| def f (α : Type u) (β : α → Type v) (a : α) (b : β a) : (a : α) × β a := | |
| ⟨a, b⟩ | |
| def g (α : Type u) (β : α → Type v) (a : α) (b : β a) : Σ a : α, β a := | |
| Sigma.mk a b | |
| #reduce f | |
| #reduce g | |
| #reduce f Type (fun α => α) Nat 10 | |
| #reduce g Type (fun α => α) Nat 10 | |
| #reduce (f Type (fun α => α) Nat 10).1 -- Nat | |
| #reduce (g Type (fun α => α) Nat 10).1 -- Nat | |
| #reduce (f Type (fun α => α) Nat 10).2 -- 10 | |
| #reduce (g Type (fun α => α) Nat 10).2 -- 10 | |
| ``` | |
| The function `f` and `g` above denote the same function. |