Notes on Category Theory Rahul Chhabra 2022-02-28 2 Contents 1 Introduction 1.1 Sensible Constructs . . . . 1.2 Well-defining Composition 1.3 Defintion of Category . . 1.4 Examples of Categories . 1.5 The Boolean Category . . 1.6 Interpreting Bool . . . . 1.7 Monoids as categories . . . . . . . . . . . . . . . . . . . . . . . 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 7 7 8 9 10 11 11 4 CONTENTS Preface This is a set of notes on category theory I’m preparing as I’m studying it. My primary texts are Category Theory for Programmers and Basic Category Theory for Computer Scientists. I do intend to move on to more advanced texts later on. The updates will be erratic. 5 6 CONTENTS Chapter 1 Introduction 1.1 Sensible Constructs I’m not going to explain why one should study Category Theory since I’m only really writing these notes for myself. I put forth the following few observations : • A vector space is defined as a construct in which linear combinations make sense. • A metric space is similarly any construct in which it is meaningful to talk about distances. So, for example, ℝ𝕟 , or any inner product space • A monoid is defined as a construct in which a binary operation has an identity and associates All of these are declarative “external views” of mathematical structures. This is similar to actually looking at an API documentation for, say, POSIX, and not caring if internally pthreads is handled by a monolithic kernel or a microkernel. We firstly intuitively define a category as a construct in which composition is a notion that makes sense. We also assert that since programming is fundamentally about composition, many programmatic constructs must find parallels in category theory (and indeed, they do!). 1.2 Well-defining Composition A vector space having a well-defined notion of linear combinations is however not coming out of the blue : any vector space must follow the vector space 7 8 CHAPTER 1. INTRODUCTION axioms. These axioms contribute to the “well-definedness”. Similarly, if our composition is going to be any good it better have at least the following properties : • composition, whatever it is, should be associative => 𝑓.(𝑔.ℎ) = (𝑓.𝑔).ℎ • we need a notion of an identity, some notion, 𝑓.𝑖 = 𝑖.𝑓 = 𝑓 So that, whatever it is that composes, should be structurally similar to monoids. (monoidoid?) 1.3 Defintion of Category We now begin with our definition of a category : Definition 1.1 (Category). A category is defined as a collection of objects along with a collection of morphisms on said objects (something like 𝑓 ∶ 𝐴 → 𝐵) such that 1. every object has exactly one associated identity morphism 2. there is some notion of compositon (.) that is associative 3. composing any morphism with an identity morphism must yield the same morphism again To make the third statement a bit more precise : consider any 𝑓 ∶ 𝐴 → 𝐵 in a category, where 𝐴 and 𝐵 are objects. By the defintion of category 1.1, we know that for 𝐴, there must be an identity morphism 𝑖𝑑𝐴 , and similarly for 𝐵. 𝑓 has source 𝐴 and target 𝐵, so what the third axiom ensures is that the identity morphism of 𝐴 will not “change anything”, that is : 𝑓.𝑖𝑑𝐴 = 𝑓 Similarly, having “reached” the target 𝐵, applying the identity morphism of 𝐵 will not affect anything : 𝑖𝑑𝐵 .𝑓 = 𝑓 Observe that composition is read right-to-left. Some texts go with a left-to-right notation. We currently leave the notion of collection informal. The formalism we choose actually has consequences and this is rather unfortunate. 1.4. EXAMPLES OF CATEGORIES 1.4 9 Examples of Categories We now consider a few examples : Example 1.1 (Void). Void is the category with 0 objects and 0 morphisms : we simply assume that such a category exists. This category is not particularly useful on it’s own but loosely corresponds to the types Nothing in Kotlin, ! in Rust and Void in Haskell. All of these types are uninhabited : they cannot have any instances (not even null!). As an example, functions in Rust that panic! always “return” an “instance” of !. ! is the uninhabited type in Rust’s type system. Example 1.2 (Unit). 𝐴 𝑖𝑑𝐴 This is a category with only one object and one morphism : the identity morphism. We will refer to it as Unit. This category loosely corresponds to the notion of Unit in languages like Kotlin, Scala, Rust and Haskell. In fact, any singleton class in an object-oriented programming language also loosely corresponds to this category. We now move on categories with exactly 2 objects. Here we get some really nice variety! Example 1.3 (Elemental Binary Category). 𝑖𝑑𝐴 𝑖𝑑𝐵 𝐴 𝐵 This category has two objects and no morphisms between them. It is not particularly interesting. It is however noteworthy in the following two ways: 1. we can represent any unordered set with this trick : just the objects and identity morphisms. 2. we can think of this category as being two Unit categories being “combined” in a way. Example 1.4 (Simple Binary Category). 𝑖𝑑𝐴 𝐴 𝑖𝑑𝐵 𝑓 𝐵 10 CHAPTER 1. INTRODUCTION This is our first slightly exciting category. We have two objects, their identity morphisms and a morphism 𝑓 ∶ 𝐴 → 𝐵. This category is very close to, but not quite the same as, the category that corresponds to the Boolean type in programming languages. 1.5 The Boolean Category Example 1.5 (Boolean Category). 𝑖𝑑𝐴 𝑖𝑑𝐵 𝑓 𝐴 𝐵 𝑔 This category is particularly interesting because if we set 𝐴 true and 𝐵 false then we effectively have a categorical model of our familiar vanilla booleans. One slight difference is our interpretation of morphisms 𝑓 and 𝑔. 𝑓 ∶ 𝐴 → 𝐵 takes true to false and is therefore the NOT operator. On the other hand, 𝑔 clearly does the “same thing” : it takes false to true and is also the NOT operator. So 𝑓 and 𝑔 are not like conventional functions but are truely categorical morphisms. We call this category Bool since it provides an interpretation for Boolean types. Our intuition does tell us something more, in our Bool category, shouldn’t NOT.NOT yield the identity? Shouldn’t NOT NOT True evaluate to True? It does! We now prove this. Theorem 1.1. In the category Bool, 𝑔𝑓 = 𝑖𝑑𝐴 and 𝑓𝑔 = 𝑖𝑑𝐵 . Proof. Let us first prove that 𝑔𝑓 = 𝑖𝑑𝐴 . Since 𝑓 ∶ 𝐴 → 𝐵 and 𝑔 ∶ 𝐵 → 𝐴, we may write 𝑔𝑓 = ℎ ∶ 𝐴 → 𝐴. However, the only morphism with source 𝐴 and sink 𝐴 is 𝑖𝑑𝐴 in category Bool. Therefore, 𝑔𝑓 and 𝑖𝑑𝐴 must be the same morphism. The proof of 𝑓𝑔 = 𝑖𝑑𝐵 follows similarly. This proof technique of looking at the source and sink of a particular morphism and then reasoning about all such morphisms (in most cases, the fact that there’s only one) is a very important. The collection of all morphisms with source 𝐴 and sink 𝐵 is called the “Homset” of 𝐴 and 𝐵 and denoted by 𝐻𝑜𝑚(𝐴, 𝐵). 1.6. INTERPRETING BOOL 1.6 11 Interpreting Bool One way to think about 𝑓 and 𝑔 is as the “branches” of a more concrete function NOT. type Boolean = True | False let not bool = match bool with | True -> False // f : A -> B | False -> True // g : B -> A As we shall see, there are many ways to think about the same category. 1.7 Monoids as categories Definition 1.2 (Monoid). A monoid is a type 𝑇 along with a binary operation ∗ ∶ (𝑇 , 𝑇 ) → 𝑇 such that there is an identity (𝑒 ∶ 𝑇 ) and ∗ is associative. As an example, let us consider the type Int % 4 consisting of 0, 1, 2, 3. This type along with 0 as the identity and inherited + as the operation forms a monoid. We represent it as a category of one object (thus the name monoid) and all the elements as morphisms. 2=1.1 3=1.2 𝐴 1 0 This way, 0 corresponds to the 𝑖𝑑𝐴 morphism. We also introduce a new morphism 1. We then define 2 = 1.1 (here, . is composition of morphisms, used to simulate +). With this same trick, we can defined 3 = 2.1 and 1.3 = 0. Also, note that if we do not have 1.𝑛 = 0 for any 𝑛 then we end up with the monoid of ℕ under + as the operation and 0 as the identity. 12 CHAPTER 1. INTRODUCTION Bibliography 13