Category theory foundations including categories, functors, natural transformations, limits, and adjunctions
Scope: Categories, functors, natural transformations, limits/colimits, adjunctions, Yoneda lemma Lines: ~450 Last Updated: 2025-10-25
Activate this skill when:
Definition: A category C consists of:
Axioms:
from abc import ABC, abstractmethod
from typing import TypeVar, Generic, Callable
A = TypeVar('A')
B = TypeVar('B')
C = TypeVar('C')
class Category(ABC):
"""Abstract base for category"""
@abstractmethod
def objects(self):
"""Return collection of objects"""
pass
@abstractmethod
def morphisms(self, source, target):
"""Return morphisms from source to target"""
pass
@abstractmethod
def compose(self, f, g):
"""Compose g ∘ f"""
pass
@abstractmethod
def identity(self, obj):
"""Return identity morphism for object"""
pass
# Example: Category of Sets
class Set_Category(Category):
"""Category Set: objects are sets, morphisms are functions"""
def objects(self):
# Conceptual: all sets (can't enumerate)
return "All sets"
def morphisms(self, source, target):
# All functions from source to target
return f"Functions: {source} → {target}"
def compose(self, f, g):
"""g ∘ f"""
return lambda x: g(f(x))
def identity(self, obj):
"""id(x) = x"""
return lambda x: x
# Example usage
Set = Set_Category()
f = lambda x: x + 1 # ℕ → ℕ
g = lambda x: x * 2 # ℕ → ℕ
h = Set.compose(f, g) # h(x) = g(f(x)) = 2(x+1)
print(h(3)) # 8
Common categories:
def category_examples():
"""Common mathematical categories"""
return {
'Set': 'Sets and functions',
'Grp': 'Groups and group homomorphisms',
'Ring': 'Rings and ring homomorphisms',
'Vect_k': 'Vector spaces over field k, linear maps',
'Top': 'Topological spaces and continuous maps',
'Poset': 'Partially ordered sets, monotone functions',
'Cat': 'Small categories and functors',
'Hask': 'Haskell types and functions (idealized)'
}
Definition: Functor F: C → D maps:
Axioms:
class Functor(ABC):
"""Abstract functor between categories"""
def __init__(self, source_category, target_category):
self.source = source_category
self.target = target_category
@abstractmethod
def map_object(self, obj):
"""F(A)"""
pass
@abstractmethod
def map_morphism(self, f):
"""F(f: A → B): F(A) → F(B)"""
pass
# Example: List functor in programming
class ListFunctor(Functor):
"""F(A) = List[A] in Hask"""
def map_object(self, type_a):
"""F(A) = List[A]"""
return f"List[{type_a}]"
def map_morphism(self, f):
"""
F(f: A → B) = map(f): List[A] → List[B]
"""
return lambda xs: [f(x) for x in xs]
# Usage
list_functor = ListFunctor(None, None)
f = lambda x: x * 2 # Int → Int
F_f = list_functor.map_morphism(f) # List[Int] → List[Int]
print(F_f([1, 2, 3])) # [2, 4, 6]
Contravariant functors: Reverse arrow direction
class ContravariantFunctor(Functor):
"""F: C^op → D (reverses arrows)"""
@abstractmethod
def map_morphism(self, f):
"""F(f: A → B): F(B) → F(A) (reversed!)"""
pass
# Example: Hom functor hom(-, B): C^op → Set
def hom_functor(B):
"""hom(-, B) sends A ↦ hom(A, B)"""
return lambda A: f"Functions from {A} to {B}"
Definition: Natural transformation η: F ⇒ G between functors F, G: C → D
`` F(A) --η_A--> G(A) | | F(f) G(f) | | v v F(B) --η_B--> G(B) `` i.e., G(f) ∘ η_A = η_B ∘ F(f)
class NaturalTransformation:
"""Natural transformation η: F ⇒ G"""
def __init__(self, source_functor, target_functor):
self.F = source_functor
self.G = target_functor
self.components = {}
def component(self, obj):
"""η_A: F(A) → G(A)"""
return self.components.get(obj)
def set_component(self, obj, morphism):
"""Define component at object"""
self.components[obj] = morphism
def verify_naturality(self, f, source_obj, target_obj):
"""Check: G(f) ∘ η_A = η_B ∘ F(f)"""
η_A = self.component(source_obj)
η_B = self.component(target_obj)
Ff = self.F.map_morphism(f)
Gf = self.G.map_morphism(f)
# Would need to check equality of composed morphisms
return "Naturality condition (conceptual check)"
# Example: List to Maybe natural transformation
# length: List[A] → Maybe[Int]
def list_to_maybe_length():
"""η: List ⇒ Maybe where η_A(xs) = Just(len(xs)) if xs non-empty"""
def component(type_a):
def eta(xs):
if xs:
return ('Just', len(xs))
else:
return ('Nothing', None)
return eta
return component
Product: A × B with projections π₁: A × B → A, π₂: A × B → B Universal property: For any Z with f: Z → A, g: Z → B, ∃! h: Z → A × B such that π₁ ∘ h = f and π₂ ∘ h = g
class Product:
"""Categorical product A × B"""
def __init__(self, A, B):
self.A = A
self.B = B
self.product = (A, B) # Conceptual tuple
def projection1(self):
"""π₁: A × B → A"""
return lambda pair: pair[0]
def projection2(self):
"""π₂: A × B → B"""
return lambda pair: pair[1]
def universal_morphism(self, f, g):
"""
Given f: Z → A, g: Z → B
Return h: Z → A × B (unique)
"""
return lambda z: (f(z), g(z))
# Example in Set
A, B = "A", "B"
prod = Product(A, B)
# Universal property: pairing
f = lambda z: f"f({z})" # Z → A
g = lambda z: f"g({z})" # Z → B
h = prod.universal_morphism(f, g) # Z → A × B
z0 = "z0"
print(h(z0)) # ('f(z0)', 'g(z0)')
print(prod.projection1()(h(z0))) # 'f(z0)' = f(z0) ✓
Coproduct (sum): A + B with injections ι₁: A → A + B, ι₂: B → A + B Universal property: Dual to product
class Coproduct:
"""Categorical coproduct A + B (disjoint union)"""
def __init__(self, A, B):
self.A = A
self.B = B
def injection1(self):
"""ι₁: A → A + B"""
return lambda a: ('Left', a)
def injection2(self):
"""ι₂: B → A + B"""
return lambda b: ('Right', b)
def universal_morphism(self, f, g):
"""
Given f: A → Z, g: B → Z
Return h: A + B → Z (unique)
"""
def h(coproduct_elem):
tag, value = coproduct_elem
if tag == 'Left':
return f(value)
else: # 'Right'
return g(value)
return h
# Example: Either a b in Haskell
# Either a b = Left a | Right b (coproduct)
Definition: Functor F: C → D is left adjoint to G: D → C (F ⊣ G) if: hom_D(F(A), B) ≅ hom_C(A, G(B)) naturally in A and B
Unit and counit:
class Adjunction:
"""F ⊣ G: Adjunction between functors"""
def __init__(self, left_adjoint, right_adjoint):
self.F = left_adjoint # F: C → D
self.G = right_adjoint # G: D → C
def unit(self):
"""η: Id ⇒ G ∘ F"""
# For each A in C, η_A: A → G(F(A))
pass
def counit(self):
"""ε: F ∘ G ⇒ Id"""
# For each B in D, ε_B: F(G(B)) → B
pass
# Example: Free-Forgetful adjunction
# Free: Set → Grp (free group functor)
# Forgetful: Grp → Set (forget group structure)
# Free ⊣ Forgetful
Definition: Monad on category C is a triple (M, η, μ) where:
Axioms:
class Monad:
"""Monad (M, η, μ)"""
def __init__(self, functor_m):
self.M = functor_m
def unit(self, x):
"""η: a → M(a) (return/pure)"""
raise NotImplementedError
def join(self, mmx):
"""μ: M(M(a)) → M(a) (flatten)"""
raise NotImplementedError
def bind(self, mx, f):
"""
(>>=): M(a) → (a → M(b)) → M(b)
bind(mx, f) = μ(M(f)(mx))
"""
# Apply f to get M(M(b)), then join
mmx = self.M.map_morphism(f)(mx)
return self.join(mmx)
# Example: List monad
class ListMonad(Monad):
"""List monad in Python"""
def unit(self, x):
"""η(x) = [x]"""
return [x]
def join(self, lists):
"""μ([[x]]) = [x] (flatten)"""
result = []
for lst in lists:
result.extend(lst)
return result
def bind(self, xs, f):
"""xs >>= f = concat(map(f, xs))"""
return self.join([f(x) for x in xs])
# Usage
list_monad = ListMonad(None)
xs = [1, 2, 3]
f = lambda x: [x, -x] # a → [a]
result = list_monad.bind(xs, f) # [1, -1, 2, -2, 3, -3]
print(result)
Statement: Natural transformations η: hom(A, -) ⇒ F correspond bijectively to elements of F(A)
def yoneda_embedding():
"""
Yoneda: C → Set^(C^op)
A ↦ hom(-, A)
Fully faithful embedding
"""
def embed(obj):
# Returns functor hom(-, obj)
return lambda x: f"hom({x}, {obj})"
return embed
Homology as functor: H_n: Top → Grp
# Conceptual: H_n functor
class HomologyFunctor(Functor):
def __init__(self, n):
self.degree = n
def map_object(self, space):
"""H_n(X) = n-th homology group"""
return f"H_{self.degree}({space})"
def map_morphism(self, f):
"""f: X → Y induces f_*: H_n(X) → H_n(Y)"""
return f"induced_homomorphism({f})"
Correspondence:
def curry_howard_lambek():
"""Three-way correspondence"""
return {
'Conjunction (A ∧ B)': 'Product type (A, B) | A × B',
'Disjunction (A ∨ B)': 'Sum type Either A B | A + B',
'Implication (A → B)': 'Function type A → B | B^A',
'True': 'Unit type () | Terminal object 1',
'False': 'Empty type Void | Initial object 0',
}
Objects and Morphisms (Category)
↓
Functors (between categories)
↓
Natural Transformations (between functors)
↓
Modifications (between natural transformations)
| Construction | Universal Property | Example | |--------------|-------------------|---------| | Product | ∀ f, g ∃! h | Cartesian product | | Coproduct | ∀ f, g ∃! h | Disjoint union | | Equalizer | Limit | Kernel | | Coequalizer | Colimit | Cokernel | | Pullback | Limit | Fiber product | | Pushout | Colimit | Gluing |
| Left Adjoint | Right Adjoint | Example | |--------------|---------------|---------| | Free | Forgetful | Free monoid ⊣ Underlying set | | Curry | Uncurry | (A → B → C) ≅ (A × B → C) | | Σ (Sum) | → (Function) | Dependent sum ⊣ Function |
❌ Ignoring universal properties: Defining product as just a tuple ✅ Product defined by universal property (unique mediating morphism)
❌ Confusing objects and morphisms: Category theory is about morphisms ✅ "Objects are completely determined by morphisms to/from them"
❌ Forgetting naturality: Just defining components of transformation ✅ Natural transformation requires commuting squares for all morphisms
❌ Treating functors as just "type constructors": Missing preservation of structure ✅ Functor preserves composition: F(g ∘ f) = F(g) ∘ F(f)
curry-howard.md - Propositions as types, category-theoretic viewtopology-algebraic.md - Functorial nature of π₁, H_*abstract-algebra.md - Categories of groups, rings, modulestype-systems.md - Categories in programming languagesformal/lean-mathlib4.md - Category theory in LeanLast Updated: 2025-10-25 Format Version: 1.0 (Atomic)