IMPORTANT NOTE: THESE NOTES ARE STILL UNDER DEVELOPMENT. PLEASE WAIT UNTIL AFTER THE LECTURE WITH HANDING ANYTHING IN, OR TREATING THE NOTES AS READY TO READ.
We've spent quite a bit of time talking about categories, and special entities in them - morphisms and objects, and special kinds of them, and properties we can find.
And one of the main messages visible so far is that as soon as we have an algebraic structure, and homomorphisms, this forms a category. More importantly, many algebraic structures, and algebraic theories, can be captured by studying the structure of the category they form.
So obviously, in order to understand Category Theory, one key will be to understand homomorphisms between categories.
1.1 Homomorphisms of categories
A category is a graph, so a homomorphism of a category should be a homomorphism of a graph that respect the extra structure. Thus, we are led to the definition:
Definition A functor from a category C to a category D is a graph homomorphism F0,F1 between the underlying graphs such that for every object :
- F1(gf) = F1(g)F1(f)
Note: We shall consistently use F in place of F0 and F1. The context should be able to tell you whether you are mapping an object or a morphism at any given moment.
1.1.1 Examples and non-examples
- Monoid homomorphisms
- Monotone functions between posets
- Pick a basis for every vectorspace, send and to the matrix representing that morphism in the chosen bases.
1.2 Interpreting functors in Haskell
One example of particular interest to us is the category Hask. A functor in Hask is something that takes a type, and returns a new type. Not only that, we also require that it takes arrows and return new arrows. So let's pick all this apart for a minute or two.Taking a type and returning a type means that you are really building a polymorphic type class: you have a family of types parametrized by some type variable. For each type
The rules we expect a Functor to obey seem obvious: translating from the categorical intuition we arrive at the rules
- andfmap id = id
- fmap (g . f) = fmap g . fmap f
data Boring a = Boring instance Functor Boring where fmap f = const Boring
data List a = Nil | Cons a (List a) instance Functor List where fmap f Nil = Nil fmap f (Cons x lst) = Cons (f x) (fmap f lst) data Maybe a = Nothing | Just a instance Functor Maybe where fmap f Nothing = Nothing fmap f (Just x) = Just (f x) data Either b a = Left b | Right a instance Functor (Either b) where fmap f (Left x) = Left x fmap f (Right y) = Right (f y) data LeafTree a = Leaf a | Node [LeafTree a] instance Functor LeafTree where fmap f (Node subtrees) = Node (map (fmap f) subtrees) fmap f (Leaf x) = Leaf (f x) data NodeTree a = Leaf | Node a [NodeTree a] instance Functor NodeTree where fmap f Leaf = Leaf fmap f (Node x subtrees) = Node (f x) (map (fmap f) subtrees)
2 The category of categories
We define a category Cat by setting objects to be all small categories, and arrows to be all functors between them. Being graph homomorphisms, functors compose, their composition fulfills all requirements on forming a category. It is sometimes useful to argue about a category CAT of all small and most large categories. The issue here is that allowing opens up for set-theoretical paradoxes.
2.1 Isomorphisms in Cat and equivalences of categories
The definition of an isomorphism holds as is in Cat. However, isomorphisms of categories are too restrictive a concept.
To see this, recall the category Monoid, where each object is a monoid, and each arrow is a monoid homomorphism. We can form a one-object category out of each monoid, and the method to do this is functorial - i.e. does the right thing to arrows to make the whole process a functor.
Specifically, if is a monoid homomorphism, we create a new functor by setting C(h)( * ) = * and C(h)(m) = h(m). This creates a functor from Monoid to Cat. The domain can be further restricted to a full subcategory OOC of Cat, consisting of all the 1-object categories. We can also define a functor by U(C) = C with the monoidal structure on U(C) given by the composition in C. For an arrow we define U(F) = F.
These functors take a monoid, builds a one-object category, and hits all of them; and takes a one-object category and builds a monoid. Both functors respect the monoidal structures - yet these are not an isomorphism pair. The clou here is that our construction of C(M) from M requires us to choose something for the one object of the category. And choosing different objects gives us different categories.
Thus, the composition CU is not the identity; there is no guarantee that we will pick the object we started with in the construction in C. Nevertheless, we would be inclined to regard the categories Monoid and OOC as essentially the same. The solution is to introduce a different kind of sameness: Definition A functor is an equivalence of categories if there is a functor and:
- A family of isomorphisms in C indexed by the objects of C, such that for every arrow .
- A family of isomorphisms in D indexed by the objects of D, such that for every arrow .
The functor G in the definition is called a pseudo-inverse of F.
2.2 Natural transformations
The families of morphisms required in the definition of an equivalence show up in more places. Suppose we have two functors and . Definition A natural transformation is a family of arrows indexed by the objects of A such that for any arrow in (draw diagram)
The commutativity of the corresponding diagram is called the naturality condition on α, and the arrow αa is called the component of the natural transformation α at the object a.
Given two natural transformations and , we can define a composition componentwise as .
Proposition The composite of two natural transformations is also a natural transformation.
Proposition Given two categories C,D the collection of all functors form a category Func(C,D) with objects functors and morphisms natural transformations between these functors.
3 Properties of functors
The process of forming homsets within a category C gives, for any object A, two different functors Hom(A, − ): and . Functoriality for Hom(A, − ) is easy: Hom(A,f) is the map that takes some and transforms it into .
Functoriality for Hom( − ,A) is more involved. We can view this as a functor either from Cop, or as a different kind of functor. If we just work with Cop, then no additional definitions are needed - but we need an intuition for the dual categories.
Alternatively, we introduce a new concept of a contravariant functor. A contravariant functor is some map of categories, just like a functor is, but such that F(1[X]) = 1[F(X)], as usual, but such that for a , the functor image is some , and the composition is F(gf) = F(f)F(g). The usual kind of functors are named covariant.
4 Applications for functors
- CS applications
- State machines and monoid actions
- * Recall that a category is called discrete if it has no arrows other than the identities. Show that a small category A is discrete if and only if every set function , for every small category B, is the object part of a unique functor . Analogously, we define a small category B to be indiscrete if for every small category A, every set function is the object part of a unique functor . Characterise indiscrete categories by the objects and arrows they have.
- Show that the category of vectorspaces is equivalent to the category with objects integers and arrows matrices.
- Prove the propositions in the section on natural transformations.
- Prove that is a natural transformation from the list functor to the maybe functor. IslistToMaybe :: [a] -> Maybe aa natural transformation? Between which functors? Find three more natural transformations defined in the standard Haskell library.catMaybes