Lecture 5: A Variety of Categories and Functors

In this lecture, we are going to give examples of categories and functors, so that whenever you hear something to do with categories or functors in the future, you can think “Oh, how does this work for categories X,Y,Z and functors F,G,H”. It is a good idea in math to always have a collection of examples for concepts which serve as “test cases”.

This lecture is going to really start picking up steam from the previous lectures. But I believe in you; you can handle it!

import Pkg; Pkg.activate(".")
include("../src/Categories.jl")
include("../src/FinSets.jl")

using .Categories
using .FinSets

Preorders

A preorder consists of

  • A set X
  • A function \leq \colon X \times X \to \{\top, \bot\} (which is how we write true and false in math)

such that

  • For all x \in X, x \leq x
  • For all x,y,z \in X, if x \leq y and y \leq z, then x \leq z.
abstract type Preorder{T} end

# leq(p::Preorder{T}, x::T, y::T)::Bool

Let A be a finite set. A subset of A is a finite set containing only elements of A. Equivalently, a subset of A is a function A \to \{\top, \bot\}.

The set of subsets of A is a preorder, where U \leq V if every element of U is an element of V.

function subsetof(U::FinSet{T}, A::FinSet{T}) where {T}
  all(x  A for x in U)
end

struct SubsetPreorder{T} <: Preorder{FinSet{T}}
  A::FinSet{T}
end

function leq(p::SubsetPreorder{T}, U::FinSet{T}, V::FinSet{T}) where {T}
  @assert subsetof(U,p.A)
  @assert subsetof(V,p.A)
  subsetof(U,V)
end

The real numbers, \mathbb{R}, are a partially ordered set, using the standard ordering.

struct RealPreorder <: Preorder{Float64}
end

leq(::RealPreorder, x::Float64, y::Float64) = x <= y

Given any preorder (X,\leq), there is a category with objects X and

  • Precisely one morphism from x to y if x \leq y
  • No morphisms from x to y if x \nleq y (if x is not less than y).

In fact, we can take this as an alternative definition of preorder: a preorder is a category where there is either one or zero morphisms between any two objects. The idea is that a morphism from x to y “witnesses” the fact that x \leq y.

We express this with Julia.

struct PreorderMorphism{T,P<:Preorder{T}}
  p::P
  dom::T
  codom::T
  function PreorderMorphism(p::Preorder{T}, dom::T, codom::T) where {T}
    # We can only create this morphism if dom ≤ codom
    @assert leq(p, dom, codom)
    new{T,typeof(p)}(p, dom, codom)
  end
end

struct PreorderAsCat{T,P<:Preorder{T}} <: Category{T,PreorderMorphism{T,P}}
  p::P
end

Categories.dom(::PreorderAsCat{T,P}, f::PreorderMorphism{T,P}) where {T,P} = f.dom
Categories.codom(::PreorderAsCat{T,P}, f::PreorderMorphism{T,P}) where {T,P} = f.codom

Categories.id(c::PreorderAsCat{T,P}, x::T) where {T,P} = PreorderMorphism(c.p, x, x)
Categories.compose(
  c::PreorderAsCat{T,P},
  f::PreorderMorphism{T,P},
  g::PreorderMorphism{T,P}
) where {T,P} = PreorderMorphism(c.p, f.dom, g.codom)

Let us now determine what a functor between two preorders would be, if we understand those preorders as categories.

A functor between preorders X and Y simply consists of a function F \colon X \to Y such that if x \leq x' for x,x' \in X, then F(x) \leq F(x').

Proof. If there is a morphism f from x to y, then there must exist a morphism F(f) from f(x) to f(y). Moreover, F can send morphisms to only one place, so it must preserve composites and identities.

We call a functor between preorders an order-preserving map.

There is a category \mathsf{Preorder} of where the objects are preorders and the morphisms are order-preserving maps.

We said that we can view preorders as categories. Let’s make this statement precise.

If \mathsf{C} is a category, then a subcategory \mathsf{D} of \mathsf{C} consists of a subset \mathsf{D}_{0} \subset \mathsf{C}_{0} of objects, and for each x,y \in \mathsf{D}_{0}, a subset \mathrm{Hom}_{\mathsf{D}}(x,y) \subset \mathrm{Hom}_{\mathsf{C}}(x,y) of morphisms, such that

  • All identities are in \mathsf{D}
  • The composite of morphisms in \mathsf{D} is again in \mathsf{D}

If \mathsf{D}_{0} = \mathsf{C}_{0}, we call \mathsf{D} wide, and if \mathrm{Hom}_{D}(x,y) = \mathrm{Hom}_{C}(x,y) for all x,y \in \mathsf{D}_{0}, we call \mathsf{D} full. The only wide, full subcategory is \mathsf{C} itself.

We now state the following proposition

\mathsf{Preorder} is a full subcategory of \mathsf{Cat}.

Now is when something very subtle comes into play. We have two different definitions of preorder, which are in some sense equivalent, but the way we have stated definitions, one of these definitions would make the previous proposition true, and another of those definitions would make the previous proposition false. Specifically, if we say that a preorder is a category with the property that each hom-set is a singleton or empty, then the set of preorders is literally a subset of the set of (small) categories. However, if we say that a preorder is a set with a function to Bool, then this is not true! Concretely, it is not true that Preorder <: Category, it’s just that we can construct a category out of a preorder!

It is for this reason in category theory that we take a slightly generalized notion of “subobject”. Instead of using subset, where the elements of set A have to be contained in set B, we just require there to be an injection from A to B! So we say that a subcategory of \mathsf{C} is just another category \mathsf{D} that has an injective functor into \mathsf{C}.

This follows from a more general principle that we should not distinguish between isomorphic objects. I.e., if we have an injective functor into \mathsf{C}, then \mathsf{D} is isomorphic to the image of that functor, which is a literal subset of \mathsf{C}.

Now that we have made this pedantic and subtle point, we will follow convention in category to ignore it, and say things like \mathsf{Preorder} is a full subcategory of \mathsf{Cat} without caring about the precise definition of preorder we have chosen.

With this out of the way, we now talk about a menagerie of functors. In category theory, often functors are described by what they do on objects, and their action on morphisms is inferred. This makes a good exercise for the reader; see if you can tell what the action on morphisms of each of the following functors is.

  1. There is a functor \mathsf{Preorder} \to \mathsf{Cat} that simply sends a preorder to that preorder viewed as a category, as we discussed before.
  2. There is a functor \mathsf{Cat} which sends a category \mathsf{C} to the preorder with the objects of \mathsf{C}, and x \leq y if and only if \mathrm{Hom}_{\mathsf{C}}(x,y) is non-empty.
  3. There is a functor \mathsf{Preorder} \to \mathsf{Set} which simply sends a preorder to its underlying set.
  4. There is a functor \mathsf{Set} \to \mathsf{Preorder} which sends a set X to the preorder with underlying set X where x \leq y if and only if x = y. Such a preorder is known as a discrete preorder.
  5. There is a functor \mathsf{Set} \to \mathsf{Preorder} which sends a set X to the preorder with underlying set X where x \leq y always. Such a preorder is known as a codiscrete preorder.

To sum up, preorders are a special case of categories, where there is at most one morphism between any two objects. We now move on to another special case of categories.

Monoids

Preorders are an “extreme” example of categories, where there are lots of objects, but very few morphisms. The other extreme is monoids, where there is only one object, but plenty of morphisms. We start with the classical definition of morphism.

A monoid is a set M along with a binary operation \ast \colon M \times M \to M and an element e \in M such that:

  • For all a,b,c \in M, (a \ast b) \ast c = a \ast (b \ast c)
  • For all a \in M, a \ast e = a = e \ast a

We call e the unit or identity element of the monoid, and \ast the multiplication.

abstract type Monoid{T} end

# mul(m::Monoid{T}, x::T, y::T)::T
# ident(m::Monoid{T})::T

Let A be an alphabet, and let A^{\ast} be the set of strings on that alphabet. For instance, if A = \{a,b,c\}, then A^{\ast} = \{[],[a],[b],[c],[aa],[ab],[ac],[ba],\ldots\}. Then if we define \ast to be “concatenation”, i.e. [ab] \ast [cba] = [abcba], and if we define e = [], (A^{\ast},\ast,[]) is a monoid.

struct ConcatMonoid{T} <: Monoid{Vector{T}}
  alphabet::Set{T}
end

function mul(m::ConcatMonoid{T}, xs::Vector{T}, ys::Vector{T}) where {T}
  @assert all(x  m.alphabet for x in xs)
  @assert all(y  m.alphabet for y in xs)
  [xs; ys]
end
ident(::ConcatMonoid{T}) where {T} = T[]

Any of \mathbb{N},\mathbb{Z},\mathbb{Q},\mathbb{R},\mathbb{C} with + or \cdot is a monoid.

The set of n \times n matrices with either matrix multiplication, element-wise multiplication, or element-wise addition is a monoid.

The set of subsets of some fixed set A with either intersection or union is a monoid. The unit of intersection is A, and the unit of union is \emptyset.

A monoid is precisely the same thing as a category with only a single object.

Proof. Suppose that \mathsf{C} is a category with one object x. Then \mathsf{Hom}_{\mathsf{C}}(x,x) is a monoid with multiplication \circ and unit 1_x. Conversely, if M is a monoid, we can construct a category with one object x, and \mathsf{Hom}_{\mathsf{C}}(x,x) = M, with \circ = \ast and 1_x = e.

Moreover, a functor between two monoids viewed as categories is a simply a function F \colon M \to N such that F(a \ast b) = F(a) \ast F(b), as the single object of the monoid has only one place to go!

Let \mathsf{Mon} be the category of monoids. Then there are a bunch of functors between \mathsf{Mon} and our old familiar categories.

  • There is a functor \mathsf{Mon} \to \mathsf{Cat} which sends a monoid to that monoid viewed as a category.
  • There is a functor \mathsf{Mon} \to \mathsf{Set} which sends a monoid to its underlying set.
  • There is a functor \mathsf{Set} \to \mathsf{Mon} which sends a set A to the monoid A^{\ast} of strings on the alphabet A.