497 lines
15 KiB
Plaintext
497 lines
15 KiB
Plaintext
#import "prooftree.typ": *
|
|
#import "@preview/showybox:2.0.1": showybox
|
|
#import "@preview/cetz:0.2.2": *
|
|
#set page(width: 5.6in, height: 9in, margin: 0.4in)
|
|
|
|
= Type theory crash course
|
|
|
|
== MLTT + Sets
|
|
|
|
Important features in MLTT:
|
|
|
|
#let Nat = $sans("Nat")$
|
|
#let Vect = $sans("Vect")$
|
|
|
|
#let Bool = $sans("Bool")$
|
|
#let tru = $sans("true")$
|
|
#let fls = $sans("false")$
|
|
|
|
- Dependent types and functions.
|
|
e.g $ "concatenate": Pi_(m,n:"Nat") Vect(m) -> Vect(n) -> Vect(m+n) $
|
|
- Function arrows always associate to the right.
|
|
- All functions are total.
|
|
|
|
Goal: to write well-typed programs. (implementing an algorithm and proving a mathematical statement are the same)
|
|
|
|
=== Judgements
|
|
|
|
$ "context" tack.r "conclusion" $
|
|
|
|
#let defeq = $equiv$
|
|
|
|
#table(
|
|
columns: 2,
|
|
stroke: gray,
|
|
$Gamma$, [sequence of variable declarations (contexts are always well-formed)],
|
|
$Gamma tack.r A$, [$A$ is well-formed *type* in context $Gamma$],
|
|
$Gamma tack.r a : A$, [*term* $a$ is well-formed and of type $A$],
|
|
$Gamma tack.r A defeq B$, [types $A$ and $B$ are *convertible*],
|
|
$Gamma tack.r a defeq b : A$, [$a$ is convertible to $b$ in type $A$],
|
|
)
|
|
|
|
Example:
|
|
|
|
#let isZero = $sans("isZero?")$
|
|
|
|
$ isZero& : Nat -> Bool \
|
|
isZero& (n) :defeq "??"
|
|
$
|
|
|
|
At this point, looking for $(n: Nat) tack.r isZero(n) : Bool$.
|
|
|
|
=== Inference rules
|
|
|
|
#prooftree(
|
|
axiom($J_1$),
|
|
axiom($J_2$),
|
|
axiom($J_3$),
|
|
rule(n: 3, $J$),
|
|
)
|
|
|
|
For example:
|
|
|
|
#prooftree(
|
|
axiom($Gamma tack.r a defeq b : A$),
|
|
rule($Gamma tack.r b defeq a : A$),
|
|
)
|
|
|
|
#let subst(name, replacement, expr) = $#expr [ #replacement \/ #name ]$
|
|
|
|
=== Interpreting types as sets
|
|
|
|
You can interpret types as sets, where $a : A$ is interpreted as $floor(a) : floor(A)$.
|
|
|
|
- Univalent mathematics can _not_ be interpreted as sets. There are extra axioms that breaks the interpretation.
|
|
- The judgement $a : A$ cannot be proved or disproved.
|
|
- For ex. 2 of natural numbers and 2 of integers can be converted but are inherently different values.
|
|
|
|
=== Convertibility
|
|
|
|
If $x : A$ and $A defeq B$, then $x : B$. We are thinking of these types as literally the same.
|
|
|
|
If $a defeq a'$ then $f @ a defeq f @ a'$.
|
|
|
|
=== Declaring types and terms
|
|
|
|
4 types of rules:
|
|
|
|
#let typeIntroTable(formation, introduction, elimination, computation) = table(
|
|
columns: 2,
|
|
stroke: 0in,
|
|
[#text(fill: blue, [Formation])], [#formation],
|
|
[#text(fill: blue, [Introduction])], [#introduction],
|
|
[#text(fill: blue, [Elimination])], [#elimination],
|
|
[#text(fill: blue, [Computation])], [#computation],
|
|
)
|
|
|
|
#typeIntroTable(
|
|
[a way to construct a new type],
|
|
[way to construct *canonical terms* of the type],
|
|
[how to use a term of the new type to construct terms of other types],
|
|
[what happens when one does Introduction followed by Elimination],
|
|
)
|
|
|
|
Example (context $Gamma$ are elided):
|
|
|
|
=== Functions
|
|
|
|
#typeIntroTable(
|
|
[If $A$ and $B$ are types, then $A -> B$ is a type
|
|
#prooftree(axiom($Gamma tack.r A$), axiom($Gamma tack.r B$), rule(n: 2, $Gamma tack.r A -> B$))],
|
|
[If $(x : A) tack.r b : B$, then $tack.r lambda (x : A) . b(x) : A -> B$
|
|
- $b$ is an expression that might involve $x$],
|
|
[If we have a function $f : A -> B$, and $a : A$, then $f @ a : B$ (or $f(a) : B$)],
|
|
[What is the result of the application? \
|
|
$(lambda(x : A) . b(x)) @ a defeq subst(a, x, b)$
|
|
- Substitution $subst(a, x, b)$ is built-in],
|
|
)
|
|
|
|
Questions:
|
|
|
|
- *What does the lambda symbol mean?* Lambda is just notation. It could also be written $tack.r "lambda"((x:A), b(x)) : A -> B$.
|
|
|
|
Another example: the singleton type
|
|
|
|
=== Unit type
|
|
|
|
#let unit = $bb(1)$
|
|
#let tt = $star$
|
|
#let rec = $sans("rec")$
|
|
#let ind = $sans("ind")$
|
|
|
|
#typeIntroTable(
|
|
[$unit$ is a type],
|
|
[$tt : unit$],
|
|
[If $x : unit$ and $C$ is a type and $c : C$, then $rec_unit (C, c, x) : C$],
|
|
[$rec_unit (C, c, t) defeq c$
|
|
- Interpretation in sets: a one-element set],
|
|
)
|
|
|
|
- Question: *How to construct this using lambda abstraction?*
|
|
- (Structural rule: having $tack.r c : C$ means $x : unit tack.r c : C$, which by the lambda introduction rule gives us $lambda x.c : unit -> C$)
|
|
|
|
=== Booleans
|
|
|
|
#typeIntroTable(
|
|
[$Bool$ is a type],
|
|
[$tru : Bool$ and $fls : Bool$],
|
|
[If $x : Bool$ and $C$ is a type and $c, c' : C$, then $rec_Bool (C, c, c', x) : C$],
|
|
[$rec_Bool (C, c, c', tru) defeq c$ and $rec_Bool (C, c, c', fls) defeq c'$],
|
|
)
|
|
|
|
=== Empty type
|
|
|
|
#let empty = $bb(0)$
|
|
|
|
#typeIntroTable(
|
|
[$empty$ is a type],
|
|
[_(no introduction rule)_],
|
|
[If $x : empty$ and $C$ is a type, then $rec_empty (C, x) : C$],
|
|
[_(no computation rule)_],
|
|
)
|
|
|
|
=== Natural numbers
|
|
|
|
#let zero = $sans("zero")$
|
|
#let suc = $sans("suc")$
|
|
|
|
#typeIntroTable(
|
|
[$Nat$ is a type],
|
|
[- $zero : Nat$
|
|
- If $n : Nat$, then $suc(n) : Nat$],
|
|
[If $C$ is a type and $c_0:C$ and $c_s:C->C$ and $x: Nat$, then $rec_Nat (C,c_0,c_s,x) : C$],
|
|
[- $rec_Nat (C, c_0, c_s, zero) defeq c_0$
|
|
- $rec_Nat (C, c_0, c_s, suc(n)) defeq c_s @ (rec_Nat (C, c_0, c_s, n))$
|
|
We can define computation rule on naturals using a universal property],
|
|
)
|
|
|
|
=== Pattern matching
|
|
|
|
*Exercise.* Define a function $isZero : Nat -> Bool$.
|
|
|
|
*Solution.* $isZero :defeq lambda (x : Nat) . rec_Nat (Bool , tru, lambda (x : Bool) . fls , x)$.
|
|
|
|
This is cumbersome and prone to mistakes. Instead we use pattern matching. A function $A -> C$ is completely specified if it's specified on the *canonical elements* of $A$.
|
|
|
|
$ isZero& : Nat -> Bool \
|
|
isZero& zero defeq tru \
|
|
isZero& suc (n) defeq fls \
|
|
$
|
|
|
|
=== Pairs
|
|
|
|
#let pair = $sans("pair")$
|
|
|
|
#typeIntroTable(
|
|
[If $A$ and $B$ are types, then $A times B$ is a type],
|
|
[If $a : A$ and $b : B$, then $pair(a, b) : A times B$],
|
|
[If $C$ is a type, and $p : A -> B -> C$ and $t : A times B$, then $rec_times (A, B, C, p, t) : C$],
|
|
[...],
|
|
)
|
|
|
|
#let fst = $sans("fst")$
|
|
#let snd = $sans("snd")$
|
|
#let swap = $sans("swap")$
|
|
#let assoc = $sans("assoc")$
|
|
|
|
*Exercise.* Define $snd : A times B -> A$ and $snd : A times B -> B$.
|
|
|
|
*Exercise.* Given types $A$ and $B$, write a function $swap$ of type $A times B -> B times A$.
|
|
|
|
*Exercise.* What is the type of $swap @ pair (tt , fls)$?
|
|
|
|
*Exercise.* Write a function $assoc$ of type $(A times B) times C -> A times (B times C)$
|
|
|
|
=== Type dependency
|
|
|
|
In particular: dependent type $B$ over $A$. (read "family $B$ of types indexed by $A$")
|
|
|
|
Example: type of vectors.
|
|
|
|
$ n : Nat tack.r Vect(n) $
|
|
|
|
=== Universes
|
|
|
|
#let Type = $sans("Type")$
|
|
|
|
There is a type $Type$. Its elements are types ($A : Type$). The dependent function $x : A tack.r B$ can be considered a function $lambda x . B : A -> Type$.
|
|
|
|
What is the type of $Type$? $Type$ is in an indexed hierarchy to avoid type-in-type paradox. We usually omit the index: $Type_i : Type_(i + 1)$
|
|
|
|
=== Dependent functions $product_(x : A) B$
|
|
|
|
#typeIntroTable(
|
|
[If $A$ is a type, and $B$ is a type family indexed by $A$, then there is a type $product_(x : A) B$],
|
|
[If $(x : A) tack.r b : B$, then $lambda (x : A) . b : product_(x : A) B$],
|
|
[If $f : product_(x : A) B$ and $a : A$, then $f(a) : subst(x, a, B)$],
|
|
[$(lambda (x : A) . b) ( a) defeq subst(x, a, b)$
|
|
- The case $A -> B$ is a special case of the dependent function, where $B$ doesn't depend on $x : A$],
|
|
)
|
|
|
|
=== Dependent pairs $Sigma_(x : A) B$
|
|
|
|
#typeIntroTable(
|
|
[If $x : A tack.r B$, then there is a type $Sigma_(x : A) B$],
|
|
[If $a : A$ and $b : B(a)$, then $pair (a, b) : Sigma_(x : A) B$],
|
|
[...],
|
|
[...
|
|
- The case $A times B$ is a special case of the dependent function, where $B$ doesn't depend on $x : A$],
|
|
)
|
|
|
|
=== Identity type
|
|
|
|
#let Id = $sans("Id")$
|
|
#let refl = $sans("refl")$
|
|
#let propeq = $=$
|
|
|
|
#typeIntroTable(
|
|
[If $a : A$ and $b : A$, then $Id_A (a, b)$ is a type],
|
|
[If $a : A$, then $refl(a) : Id_A (a, a)$],
|
|
[
|
|
- If $(x, y : A), (p : Id_A (x, y)) tack.r C (x, y, p)$
|
|
- and $(x : A) tack.r t(x) : C(x, x, refl(x))$
|
|
- then $(x, y : A), (p : Id_A (x, y)) tack.r ind_Id (t; x, y, p) : C(x, y, p)$],
|
|
[...],
|
|
)
|
|
|
|
- (There are dependent versions of each of the previous elimination rules that resulted in $C$)
|
|
- $ind$ "induction" is used more when the $C$ is dependent, while $rec$ is better for non-dependent $C$'s.
|
|
|
|
For example, to define:
|
|
|
|
#let sym = $sans("sym")$
|
|
|
|
$ sym : product_(x, y : A) Id(x, y) -> Id(y, x) $
|
|
|
|
it suffices to just specify its image on $(x, x, refl)$
|
|
|
|
#prooftree(
|
|
axiom($x : A tack.r refl(x) : Id(x, x)$),
|
|
rule($(x, y : A) , (p : Id(x, y)) tack.r Id(y, x)$),
|
|
rule($x, y : A tack.r Id(x, y) -> Id(y, x)$),
|
|
rule($sym : product_(x, y : A) Id(x, y) -> Id(y, x)$)
|
|
)
|
|
|
|
So $sym(x, x, refl(x)) :defeq refl(x)$
|
|
|
|
#let transport = $sans("transport")$
|
|
|
|
*Exercise.* Define $transport^B : product_(x, y : A) Id(x, y) -> B(x) -> B(y)$.
|
|
|
|
*Exercise: swap is involutive.* Given types $A$ and $B$, write a function $product_(t : A times B) Id(swap(swap(t)), t)$
|
|
|
|
=== Disjoint sum
|
|
|
|
#let inl = $sans("inl")$
|
|
#let inr = $sans("inr")$
|
|
|
|
#typeIntroTable(
|
|
[If $A$ and $B$ are types, then $A + B$ is a type],
|
|
[
|
|
- If $a : A$, then $inl(a) : A + B$
|
|
- If $b : B$, then $inr(b) : A + B$
|
|
],
|
|
[If $f : A -> C$ and $g : B ->C $ then $rec_+ (C, f, g) : A + B -> C$],
|
|
[
|
|
- $rec_+ (C, f, g)( inl(a)) defeq f(a)$
|
|
- $rec_+ (C, f, g)( inr(b)) defeq g(a)$
|
|
],
|
|
)
|
|
|
|
=== Interpreting types as sets and propositions
|
|
|
|
#table(
|
|
columns: 3,
|
|
stroke: 0in,
|
|
[$A$], [set $A$], [proposition $A$],
|
|
[$a:A$], [$a in A$], [$a$ is a proof of $A$],
|
|
[$unit$], [], [$top$],
|
|
[$empty$], [], [$bot$],
|
|
[$A times B$], [cartesian product], [$A and B$],
|
|
[$A + B$], [disjoint unions $A product.co B$], [$A or B$],
|
|
[$A -> B$], [set of functions $A -> B$], [$A => B$],
|
|
[$x : A tack.r B(x)$], [], [predicate $B$ on $A$],
|
|
[$sum_(x : A) B(x)$], [], [$exists x in A, B(x)$],
|
|
[$product_(x : A) B(x)$], [], [$forall x in A, B(x)$],
|
|
[$Id_A (a, b)$], [], [equality $a = b$],
|
|
)
|
|
|
|
The connectives $forall$ and $exists$ behave constructively.
|
|
|
|
The interpretation into propositions is known as the *Curry-Howard correspondence*.
|
|
|
|
=== Negation
|
|
|
|
$ not A :defeq A -> empty $
|
|
|
|
*Exercise.* Construct a term of type $A -> not not A$.
|
|
|
|
*Exercise ($tru eq.not fls$).* Construct a term of type $not Id(tru, fls)$.
|
|
|
|
*Solution.* Let $B :defeq rec_Bool (Type, unit, empty)$. Then $B(tru) defeq unit$ and $B(fls) defeq empty$. Then:
|
|
|
|
$ lambda (p : Id(tru, fls)) . transport^B (p, tt) : Id(tru, fls) -> empty $
|
|
|
|
== Univalent Foundations
|
|
|
|
Voevodsky at HLF 2016:
|
|
|
|
- Math is the study of structures on sets and their higher analogs.
|
|
- Set-theoretic math is a subset of math that can be expressed in univalent foundations.
|
|
- Classic math is a subset of univalent math consisting of results that require LEM / AC.
|
|
|
|
=== Identities as paths
|
|
|
|
Identity behaves like classic equality in these ways:
|
|
|
|
- Reflexivity, symmetry, transitivity
|
|
- $transport^B : B(x) times Id(x, y) -> B(y)$
|
|
|
|
Identity also does _not_ behave like classic equality in these ways:
|
|
|
|
- There can be two different identities $p, q : Id(x, y)$
|
|
- There can be identities of identities $ a : Id_(Id(x, y)) (p, q) $
|
|
(but there don't have to be)
|
|
|
|
So instead of treating them just as equality, we treat them kind of as homotopy, which is another notion of sameness coming from algebraic topology.
|
|
|
|
// #canvas({
|
|
// import draw: *
|
|
// fill(black)
|
|
// circle((0, 0), radius: 0.1)
|
|
// circle((3, 0), radius: 0.1)
|
|
// line((0.25, 0), (2.75, 0))
|
|
// })
|
|
|
|
There's always a point $refl$ that stays at point $x$.
|
|
|
|
=== Operations on paths
|
|
|
|
#let pathto = $op(arrow.squiggly.r.long)$
|
|
|
|
Symmetry: if we have $p : x pathto y$, then we have $sym(p) : y pathto x$
|
|
|
|
Transport: transport sends a point $b : B(x)$ to a point $transport^B (p, b) : B(y)$.
|
|
|
|
Functions map paths, not just points. If $p : x pathto y$, and $f : A -> B$, then $f(p) : f(x) pathto f(y)$
|
|
|
|
=== Path concatenation
|
|
|
|
Paths have
|
|
|
|
- Associativity ($(p dot q) dot r pathto p dot (q dot r)$)
|
|
- Identity ($p dot 1 pathto p$, $1 dot p pathto p$)
|
|
- Inverse ($p dot p^-1 pathto 1$, $p^-1 dot p pathto 1$)
|
|
|
|
*Theorem (Garner, van den Berg).* $(A, pathto_A, pathto_(pathto_A), ...)$ forms an $infinity$-groupoid (groupoid laws hold up to higher paths).
|
|
|
|
=== Interpreting types as topological spaces
|
|
|
|
There's an interpretation of univalent foundations into the category of _Kan complexes_.
|
|
|
|
(cannot interpret in the category of topological spaces for some technical reasons, ignore and it's ok to use this for intuition only)
|
|
|
|
=== Interpretation as simplicial sets
|
|
|
|
#table(
|
|
columns: 2,
|
|
stroke: 0in,
|
|
[$(A, pathto_A, pathto_(pathto_A), ...)$], [Kan complex $A$],
|
|
[$a:A$], [$a in A_0$],
|
|
[$A times B$], [binary product],
|
|
[$A + B$], [binary coproduct],
|
|
[$A -> B$], [space of maps],
|
|
[$x : A tack.r B(x)$], [fibration $B -> A$ with fibers $B(x)$],
|
|
[$sum_(x : A) B(x)$], [total space of fibration $B -> A$],
|
|
[$product_(x : A) B(x)$], [space of sections of fibration $B -> A$],
|
|
)
|
|
|
|
Question: more intuition on fibration?
|
|
|
|
- Fibration is something that has the transport property. A fibration is a _map_ in the category (i.e $B -> A$).
|
|
|
|
#image("fibration.jpeg", height: 3in)
|
|
|
|
Path lifting can be done over fibers in a fibration such as $B :defeq sum_(a : A) B(a)$
|
|
|
|
=== Contractible types
|
|
|
|
#let isContr = $sans("isContr")$
|
|
|
|
*Definition (contractible).* A type $A$ is contractible if: $ isContr(A) :defeq sum_(x : A) product_(y : A) y pathto x $
|
|
|
|
In homotopy theory, you can contract a space into a single point. This is also called a *singleton*. The specified point $a$ is called the "center" of contraction, and there are paths from any other $y : A$ to $x$.
|
|
|
|
Question: do you have to give the point?
|
|
|
|
- In constructive logic, you must explicitly give the point that it is contracted to, cannot just prove with contradiction.
|
|
|
|
=== Equivalence
|
|
#let isequiv = $sans("isequiv")$
|
|
|
|
*Definition (equivalence).* A map $f : A -> B$ is an *equivalence* if it has contractible fibers.
|
|
|
|
$ isequiv(f) :defeq product_(b : B) isContr(sum_(a : A) f(a) pathto b) $
|
|
|
|
Fix $b$, the sigma is the preimage of the point $b$. For all $b$, all preimages are contractible.
|
|
|
|
#let eqv = $tilde.eq$
|
|
|
|
$ A eqv B :defeq sum_(f : A -> B) isequiv(f) $
|
|
|
|
There are also other definitions of equivalence.
|
|
|
|
*Exercise.* Show that 1 is contractible.
|
|
|
|
*Exercise.* Let $A$ be a contractible type. Construct an equivalence $A eqv 1$
|
|
|
|
For paths between pairs, in the dependent version, we have
|
|
|
|
$ ((a, b) pathto (a' , b')) eqv sum_(p : a pathto a') transport^B (p, b) pathto b' $
|
|
|
|
This is required because $b : B(a)$ but $b' : B(a')$, and we can't write paths between different types.
|
|
So instead, we use transport to get back into the correct type: $transport^B (p, b) : B(a')$
|
|
|
|
=== Path types of identity types
|
|
|
|
Cannot show uniqueness of identity proofs (UIP): $ product_(a, b: A) product_(p, q : a pathto b) p pathto q $
|
|
|
|
(Lean assumes UIP)
|
|
|
|
=== Path types of the universe
|
|
|
|
#let idtoequiv = $sans("idtoequiv")$
|
|
|
|
$ idtoequiv : product_(A, B : Type) (A pathto B) -> (A eqv B) $
|
|
|
|
*Definition (univalence).* $idtoequiv$ is an equivalence.
|
|
|
|
- Question: What is the benefit of UIP over univalence?
|
|
- UIP gives set theory
|
|
|
|
- Question: what if you had a path between $A pathto B$ and $A eqv B$?
|
|
- You can use univalence to get a path. But we shouldn't assume an equality without first using $idtoequiv$.
|
|
|
|
=== Propositional truncation
|
|
|
|
#let trunc(A) = [$bar.double$#A$bar.double$]
|
|
|
|
#typeIntroTable(
|
|
[If $A$ is a type, then $trunc(A)$ is a type],
|
|
[If $a:A$, then $overline(a):trunc(A)$ and $ p(A) : product_(x, y: trunc(A)) x pathto y $],
|
|
[If $f:A->B$ and $B$ is a prop, then we have $overline(f) : trunc(A) -> B$],
|
|
[$overline(f)(overline(a)) defeq f(a)$],
|
|
)
|
|
|
|
- $p(A)$ turns $trunc(A)$ into a proposition
|
|
- $trunc(A)$ is empty if $A$ is, and contractible if $A$ has one element. |