Lecture 3: Category Theory

Julia from last lecture
abstract type 𝔽 end

struct Vec𝔽 <: 𝔽
  elems::Vector{Any}
end

Base.:()(a, A::Vec𝔽) = a  A.elems

Base.iterate(A::Vec𝔽) = iterate(A.elems)
Base.iterate(A::Vec𝔽, k) = iterate(A.elems, k)
Base.length(A::Vec𝔽) = length(A.elems)

struct Int𝔽 <: 𝔽
  n::Int
end

Base.:()(a, A::Int𝔽) = 1 <= a <= A.n

Base.iterate(A::Int𝔽) = iterate(1:A.n)
Base.iterate(A::Int𝔽, k) = iterate(1:A.n, k)

struct 𝔽Mor
  dom::𝔽
  codom::𝔽
  vals::Dict{Any,Any}
end

(f::𝔽Mor)(x) = f.vals[x]

struct Int𝔽Mor
  dom::Int𝔽
  codom::Int𝔽
  vals::Vector{Int}
end

(f::Int𝔽Mor)(i) = f.vals[i]

Sets

Today we will be moving on from finite sets to talk about sets which are possibly infinite. Infinite sets are a controversial topic; they don’t exist as tangibly as finite sets, and generally are much trickier to compute with. For a mathematician, this is not a problem; they write down formulas for infinite sets in precisely the same way they wrote down formulas for finite sets.

The crucial difference between finite sets and infinite sets is that any “for all” statement on an infinite set is not checkable by listing out every element of the set and checking that a property holds of each of them. Relatedly, any “exists” statement is likewise not checkable. This seems kind of obvious, but it means that naive extensions of algorithms from the case of finite sets to the case of infinite sets often fails.

Thus, on a computer infinite sets have a very different feel than finite sets.

In traditional foundations, everything is built out of sets. But, as we said in the previous lecture, this is unnecessarily strict. We are instead taking Julia to be our foundation, so that we will have primitive things that are not themselves sets. This is a much saner foundation, because while it might be technically possible to build, say, a real number out of sets, nobody really wants to do that.

A set X consists of a function from Any to Bool, which may or either be written in Julia or defined mathematically. If X(x) = \mathbf{true}, we write x \in X, and if X(x) = \mathbf{false} we write x \notin X.

Note that when we write down Julia definitions involving sets, we are implicitly assuming that the functions are written in Julia. However, there are some sets that we will use whose functions cannot be written down in Julia, so take the Julia definitions with a grain of salt. If the function defining a set can be written in Julia, we say that set is computable, and otherwise the set is uncomputable.

Obviously, we can only model the computable sets in Julia, but the uncomputable sets are still useful for talking about Julia.

abstract type ComputableSet end

# We expect any subtype of ComputableSet to have in defined on it.
Base.in(x, s::ComputableSet) = error("no specific definition found")

For any finite set A, let \chi_{A} be defined by \chi_{A}(x) = \mathbf{true} if x is listed in A and \mathbf{false} otherwise. Then \chi_{A} is a set.

Any Julia type T defines a set, via the function which checks whether a Julia value has that type.

struct FiniteSet <: ComputableSet
  A::𝔽
end

Base.in(x, χ::FiniteSet) = x  χ.A

struct TypeSet <: ComputableSet
  T::Type
end

Base.in(x, χ::TypeSet) = x isa χ.T

Given two sets A and B, the set A \to B consists of all Julia callables f such that f(a) \in B for all a \in A. Note that

Note that even if A and B are computable sets, there is no way to check in Julia that a given f is an element of A \to B because this would involve iterating through possibly infinitely many elements of A. Again, languages where this is not the case don’t have good ODE solvers.

Given two sets X and Y, their intersection X \cap Y is defined by

(X \cap Y)(x) = X(x) \wedge Y(x)

Their union X \cup Y is defined by

(X \cup Y)(x) = X(x) \vee Y(x)

struct IntersectionSet
  X::ComputableSet
  Y::ComputableSet
end

Base.in(x, χ::IntersectionSet) = x  χ.X && x  χ.Y

struct UnionSet
  X::ComputableSet
  Y::ComputableSet
end

Base.in(x, χ::UnionSet) = x  χ.X || x  χ.Y

Given two sets X and Y, their product X \times Y is the set of tuples (x,y) where x \in X and y \in Y. That is, z \in X \times Y if and only if z isa Tuple, length(z) = 2, z[1] \in X and z[2] \in Y.

product(X,Y) = z -> (z isa Tuple) &&
  length(z) == 2 && X(z[1]) && Y(z[2])

We leave it to the reader to give a mathematical definition for sum given the following definition in Julia

struct Left
  val::Any
end

struct Right
  val::Any
end

sum(X,Y) = x ->
  if x isa Left
    X(x.val)
  elseif x isa Right
    Y(x.val)
  else
    false
  end

We will now move on to categories. But I would like to make one brief point first, which is that there is no set of all sets. This is because not all sets correspond to Julia values, as we consider uncomputable sets to be sets. But there is a set of all computable sets.

Categories

The moment we’ve all be waiting for.

A small category C consists of

  • a set C_0 of objects
  • for every x,y \in C_0, a set \mathrm{Hom}_C(x,y) of morphisms from x to y
  • for every x,y,z \in C_0, a composition function \circ \colon \mathrm{Hom}_C(y,z) \times \mathrm{Hom}_(x,y) \to \mathrm{Hom}(x,z)
  • for every x \in C_0, an identity morphism 1_x \in \mathrm{Hom}_C(x,x)

such that

  • for all x,y,z,w \in C_0, f \in \mathrm{Hom}_C(x,y), g \in \mathrm{Hom}_C(y,z), h \in \mathrm{Hom}_C(z,w), h \circ (g \circ f) = (h \circ g) \circ f
  • for all x,y \in C_0, f \in \mathrm{Hom}_C(x,y), 1_y \circ f = f = f \circ 1_x

These two laws are called the associativity law and unitality law respectively.

(Julia to be livecoded)

There is a category \mathsf{Fin} where the objects are finite sets and the morphisms are morphisms of finite sets, as defined in the previous lecture.

(Julia to be livecoded)

There is a category where the objects are natural numbers and a morphism from n to m is a n \times m matrix. Composition of a n \times m matrix and an m \times r matrix is done by matrix multiplication.

(Julia to be livecoded)

A graph consists of a finite set of vertices, a finite set of edges, and a source and target map from vertices to edges. Given any graph, there is a “free category” on that graph, where the objects are vertices and the morphisms are paths.

(Julia to be livecoded)