A first look at category theory
While many of functional programming concepts are derived from category theory, we often hear that it’s not necessary to learn it if we want to understand its concepts. This is, of course, is not true.
If Haskell is not a language of our choice, we are forced to map Haskell patterns to our language while these patterns are mapped to Haskell from category theory. A lot of important information is lost in translation.
This is why we start not from programming but from the theory itself. We will see that the theory is not only about programming but we will get back to some code samples at the end of the article.
When we formalize our ideas, our understanding is almost always clarified.
– David I. Spivak in Category Theory for Scientists
In fact, category theory is not so hard to grasp like we may think about. Some even think they can teach Quantum Mechanics in kindergarten with it. The theory is intended to make things simpler, by using the right “language” and formalism.
Things are getting more complicated if we map the theory implementation to the theory instead of mapping the theory to things we implement.
The purpose of this article is to write down very basic concepts of category Theory and only then get back to examples in the programming world.
Category Theory 101
Category Theory formalizes mathematical structure and its concepts in terms of a collection of objects and of arrows (also called morphisms).
Category theory is an area of mathematics that studies the properties of relationships between mathematical objects, which do not depend on the internal structure of these objects.
Objects and Morphisms
Let’s take an A and B. These are two objects, mathematical structures, types — whatever. No matter how do we call it because we don’t care about theirs internal structure.
Now let’s draw a line between A and B and name it f:
If we think about the morphism as about a function from A to B, we can also think about A and B as about types or sets:
f : A → B
Now take another object, let’s call it C and let’s draw a g : B → C:
We can easily see that if we follow the arrows f and g we can get from A to C via B, because the end of f is the same as the beginning of g. Beginning of every morphism is called a domain (dom) and ending is a codomain (cod). So we can say that:
A = dom(f), B = cod(f)
and for f : A → B and g : B → C
cod(f) = dom(g)
Joining two morphisms that match with their domain and codomain is called composition and denoted as:
g ∘ f
which produces a new morphism (function). So that:
∀ a ∈ A, (g ∘ f)(a) = g(f(a))
which means: for every a in A, a composition of g after f applied to a is the same as the result of g(f(a)).
In this way, we can connect as many objects as we need. For example:
h : C → D
Operator ∘ is associative:
(h ∘ g) ∘ f = h ∘ (g ∘ f)
∵ ∀ a ∈ A, ((h ∘ g) ∘ f)(a) = h(g(f(a)))=(h ∘ (g ∘ f))(a)
Every object we introduced has an Identity morphism:
1A : A → A
∀ a ∈ A, 1A(a) = a
Identities are composable as usuall morphisms:
f ∘ 1A = f = 1B ∘ f
Now we have everything to define a category.
A category consists of following:
- Objects: A,B,C,…
- Morphisms: f,g,…
- Identities for every object: 1A, 1B, 1C,…
The following laws must be satisfied:
(∀ f : A → B, g : B → C, h : C → D), h ∘ (g ∘ f) = (h ∘ g) ∘ f
Which means: for all the functions that are composable with each other composition is associative.
(∀ f : A → B), f ∘ 1A = f = 1B ∘ f
Which means: every function f : A → B is equal to f ∘ 1A and is equal to 1B ∘ f.
So generally speaking, a category is a set of objects connected with arrows and limited by associativity and unit laws.
Of course, there is a lot more interesting in category theory, but these basics are enough to start thinking about more complex concepts. All these monoids, functors, monads etc. are just about connecting objects with arrows and this is not only about programming.
Get into the code
It’s natural to map these concepts to any of modern programming languages.