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 welltyped 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 wellformed)],


$Gamma tack.r A$, [$A$ is wellformed *type* in context $Gamma$],


$Gamma tack.r a : A$, [*term* $a$ is wellformed 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 builtin],


)




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 oneelement 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 typeintype 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 nondependent $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 *CurryHoward 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.


 Settheoretic 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. 