Page 1
Category Theory for beginners
Melbourne Scala User Group Feb 2015@KenScambler
A B
C
Page 2
Abstract maths… for us?Dizzyingly abstract branch of maths“Abstract nonsense”?Programming = mathsProgramming = abstractionReally useful to programming!
Page 3
The planBasic Category Theory conceptsNew vocabulary (helpful for further reading)How it relates to programmingCategory Theory as seen by maths versus FP
Page 4
A bit of background1940s Eilenberg, Mac Lane invent Category Theory1958 Monads discovered by GodementIn programming:
1990 Moggi, Wadler apply monads to programming2006 “Applicative Programming with Effects” McBride & Paterson2006 “Essence of the Iterator Pattern” Gibbons & Oliveira
Page 7
CategoryObjectsArrows or morphisms
Page 8
CategoryObjectsArrowsDomain f
dom(f)
Page 9
CategoryObjectsArrowsDomain/Codomain
f
cod(f)
dom(f)
Page 10
CategoryObjectsArrowsDomain/Codomain
dom(g)
cod(g)
g
Page 11
CategoryObjectsArrowsDomain/Codomain
Page 12
CategoryObjectsArrowsDomain/CodomainComposition
f
Page 13
CategoryObjectsArrowsDomain/CodomainComposition
fg
Page 14
CategoryObjectsArrowsDomain/CodomainComposition
fg
g ∘ f
Page 15
CategoryObjectsArrowsDomain/CodomainComposition
f
Page 16
CategoryObjectsArrowsDomain/CodomainComposition
f
h
Page 17
CategoryObjectsArrowsDomain/CodomainComposition
f
h
h ∘ f
Page 18
CategoryObjectsArrowsDomain/CodomainCompositionIdentity
Page 19
CategoryCompose∘ : (B C) (A B) (A C)Identity
id : A A
Page 20
Category LawsAssociative Law
(f ∘ g) ∘ h = f ∘ (g ∘ h )
Identity Laws
f ∘ id = id ∘ f = f
Page 21
Associative law(f ∘ g) ∘ h = f ∘ (g ∘ h )
f
g
h
(g ∘ h)
(f ∘ g)
Page 22
Associative law(f ∘ g) ∘ h = f ∘ (g ∘ h )
f
g
h
(g ∘ h)
(f ∘ g)
Page 23
Associative law(f ∘ g) ∘ h = f ∘ (g ∘ h )
f
g
h
(g ∘ h)
(f ∘ g)
Page 24
Associative law(f ∘ g) ∘ h = f ∘ (g ∘ h )
f
g
h
(g ∘ h)
(f ∘ g)
Page 25
Associative law(f ∘ g) ∘ h = f ∘ (g ∘ h )
f
g
h
(g ∘ h)
(f ∘ g)
Page 26
Associative law(f ∘ g) ∘ h = f ∘ (g ∘ h )
f
g
h
(g ∘ h)
(f ∘ g)
Page 27
Associative law(f ∘ g) ∘ h = f ∘ (g ∘ h )
f
g
h
(g ∘ h)
(f ∘ g)
Page 28
Identity lawsf ∘ id = id ∘ f = f
fid id
Page 29
Identity lawsf ∘ id = id ∘ f = f
fid id
Page 30
Identity lawsf ∘ id = id ∘ f = f
fid id
Page 31
Identity lawsf ∘ id = id ∘ f = f
fid id
Page 32
ExamplesInfinite categoriesFinite categoriesObjects can represent anythingArrows can represent anythingAs long as we have composition and identity!
Page 33
Sets & functionsPerson
String Integer
bestFriend
length
name age+1
Page 34
Sets & functionsInfinite arrows from composition+1∘ length ∘ namebestFriend ∘ bestFriendbestFriend ∘ bestFriend ∘ bestFriend+1∘ age∘ bestFriend
Page 35
Sets & functionsObjects Arrows Composition Identity
Page 36
Sets & functionsObjects = sets (or types)Arrows = functionsComposition = function compositionIdentity = identity function
Page 41
Class hierarchyIFruit
IBanana
AbstractBanana
BananaImpl MockBanana
Tool
Spanner
Page 42
Class hierarchyObjects Arrows Composition Identity
Page 43
Class hierarchyObjects = classesArrows = “extends”Composition = “extends” is transitiveIdentity = trivial
Page 44
Class hierarchy Partially ordered sets (posets)Objects = elements in the setArrows = ordering relation ≤Composition = ≤ is transitiveIdentity = trivial
Page 45
World Wide Webwww.naaawcats.com
No dogs allowed!
www.robodogs.com
See here for more robots
www.coolrobots.com
BUY NOW!!!!
Page 46
World Wide WebObjects = webpagesArrows = hyperlinksComposition = Links don’t composeIdentity
Page 47
World Wide Web Graphs
Objects = nodesArrows = edgesComposition = Edges don’t composeIdentity
Page 48
“Free Category” from graphs!
Objects = nodesArrows = paths (0 to many edges)Composition = aligning paths end to endIdentity = you’re already there
Page 49
Categories in codetrait Category[Arrow[_,_]] { def compose[A,B,C]( c: Arrow[B,C], d: Arrow[A,B]): Arrow[A,C] def id[A]: Arrow[A,A]}
Page 50
Category of Types & Functions
object FnCat extends Category[Function1] { def compose[A,B,C]( c: B => C, d: A => B): A => C = { a => c(d(a)) } def id[A]: A => A = (a => a)}
Page 51
Category of Garden Hoses
sealed trait Hose[In, Out] { def leaks: Int def kinked: Boolean def >>[A](in: Hose[A, In]): Hose[A, Out] def <<[A](out: Hose[Out, A]): Hose[In, A]}
Page 52
Category of Garden Hoses
[live code example]
Page 53
Categories embody the principle of strongly-typed composability
Page 55
FunctorsFunctors map between categoriesObjects objectsArrows arrowsPreserves composition & identity
Page 56
Functor lawsComposition Law
F(g ∘ f) = F(g) ∘ F(f)Identity Law
F(idA) = idF(A)
Page 57
C F DCategory CategoryFunctor
Page 58
C F DCategory CategoryFunctor
CatCategory of categories
Page 59
C F DCategory CategoryFunctor
CatCategory of categories
Objects = categoriesArrows = functorsComposition = functor compositionIdentity = Identity functor
Page 60
C F DA
B
C
g ∘ f
f
g
Page 61
C F DA
B
C
g ∘ f
f
g
F(A)
Page 62
C F DA
B
C
g ∘ f
f
g
F(A)
F(B)
Page 63
C F DA
B
C
g ∘ f
f
g
F(A)
F(B)
F(C)
Page 64
C F DA
B
C
g ∘ f
f
g
F(A)
F(B)
F(C)
F(f)
Page 65
C F DA
B
C
g ∘ f
f
g
F(A)
F(B)
F(C)
F(f)
F(g)
Page 66
C F DA
B
C
g ∘ f
f
g
F(A)
F(B)
F(C)
F(f)
F(g)
F(g ∘ f)
Page 67
g ∘ f
f
g
F(f)
F(g)
F(g ∘ f)
Composition LawF(g ∘ f) = F(g) ∘ F(f)
Page 68
g ∘ f
f
g
F(f)
F(g)
F(g ∘ f)
Composition LawF(g ∘ f) = F(g) ∘ F(f)
Page 69
g ∘ f
f
g
F(f)
F(g)
F(g ∘ f)
Composition LawF(g ∘ f) = F(g) ∘ F(f)
Page 70
g ∘ f
f
g
F(f)
F(g)
F(g ∘ f)
Composition LawF(g ∘ f) = F(g) ∘ F(f)
Page 71
g ∘ f
f
g
F(f)
F(g)
F(g ∘ f)
Composition LawF(g ∘ f) = F(g) ∘ F(f)
Page 72
g ∘ f
f
g
F(f)
F(g)
F(g ∘ f)
Composition LawF(g ∘ f) = F(g) ∘ F(f)
Page 73
g ∘ f
f
g
F(f)
F(g)
F(g ∘ f)
Composition LawF(g ∘ f) = F(g) ∘ F(f)
Page 74
g ∘ f
f
g
F(f)
F(g)
F(g ∘ f)
Composition LawF(g ∘ f) = F(g) ∘ F(f)
Page 75
g ∘ f
f
g
F(f)
F(g)
Composition LawF(g ∘ f) = F(g) ∘ F(f)
F(g) ∘ F(f)
Page 76
g ∘ f
f
g
F(f)
F(g)
Composition LawF(g ∘ f) = F(g) ∘ F(f)
F(g) ∘ F(f)F(g ∘ f)
Page 77
Identity lawF(idA)= idF(A)
A
Page 78
Identity lawF(idA)= idF(A)
AidA
Page 79
Identity lawF(idA)= idF(A)
AidA
F(idA)
Page 80
Identity lawF(idA)= idF(A)
A
Page 81
Identity lawF(idA)= idF(A)
A F(A)
Page 82
Identity lawF(idA)= idF(A)
A F(A)
idF(A)
Page 83
Identity lawF(idA)= idF(A)
A F(A)
idF(A)
AidA
F(idA)
Page 84
Terminology
homomorphism
Page 85
Terminology
homomorphismSame
Page 86
Terminology
homomorphismSame-shape-
ism
Page 87
Terminology
homomorphism“structure preserving map”
Page 88
Terminology
homomorphismFunctors are
“category homomorphisms”
Page 89
Functors in code
trait Functor[F[_]] { def map[A,B](fa: F[A], f: A => B): F[B]}
Page 90
Functors in code
trait Functor[F[_]] { def map[A,B](fa: F[A], f: A => B): F[B]}
Objects to objects
Page 91
Functors in code
trait Functor[F[_]] { def map[A,B](fa: F[A], f: A => B): F[B]}
Arrows to arrows
Page 92
Functors in code
trait Functor[F[_]] { def map[A,B]: (A => B) => (F[A] => F[B])}
Arrows to arrows
Page 93
Functors laws in code
fa.map(f).map(g) ==
fa.map(g compose f)
Page 94
Functors laws in code
fa.map(a => a) == fa
Page 95
Terminology
endomorphism
Page 96
Terminology
endomorphismWithi
n
Page 97
Terminology
endomorphismWithi
n-shape-ism
Page 98
Terminology
endomorphism
“a mapping from something back to itself”
Page 99
Terminology
endo
“a mapping from something back to itself”
Page 100
EndofunctorsIn Scala, all our functors are actually endofunctors.
Type
F
Category Category
Endofunctor Type
Page 101
EndofunctorsLuckily, we can represent any functor in our type system as some F[_]Typ
eF
Category Category
Endofunctor Type
Page 102
List Functor
sealed trait List[+A]case class Cons(head: A, tail: List[A]) extends List[A]case object Nil extends List[Nothing]
Page 103
List Functor
sealed trait List[+A] { def map[B](f: A => B): List[B] = this match { case Cons(h,t) => Cons(f(h), t map f) case Nil => Nil } }}
Page 104
List Functor
potatoList .map(mashEm) .map(boilEm) .map(stickEmInAStew)
Page 105
List Functor
userList .map(_.name) .map(_.length) .map(_ + 1) .map(_.toString)
Page 106
Other functorstrait Tree[A]trait Future[A]trait Process[A]trait Command[A]X => A(X, A)trait Option[A]
Page 107
FunctorsFundamental concept in Category TheorySuper usefulEverywhereStaple of functional programmingWrite code that’s ignorant of unnecessary context
Page 109
MonoidsSome set we’ll call MCompose
• : M × M M
Identity
id : M
Page 110
Monoid LawsAssociative Law
(f • g) • h = f • (g • h )
Identity Laws
f • id = id • f = f
Page 111
Category LawsAssociative Law
(f ∘ g) ∘ h = f ∘ (g ∘ h )
Identity Laws
f ∘ id = id ∘ f = f
Page 112
MonoidsCompose
• : M × M M
Identity
id : M
Page 113
CategoryCompose∘ : (B C) (A B) (A C)Identity
id : A A
Page 114
Category with 1 objectCompose∘ : (A A) (A A) (A A)Identity
id : A A
Page 115
Category with 1 objectCompose∘ : M M MIdentity
id : M
Page 116
Monoids are categories
Each arrow is an element in the monoid
Only one object
Page 117
Monoids are categories
Objects = placeholder singletonArrows = elements of the monoidComposition = •Identity = id
Only one object
Each arrow is an element in the monoid
Page 118
M H NMonoid Monoid
Monoid homomorphism(SM, •M, idM) (SN, •N, idN)
Page 119
M H NMonoid Monoid
Monoid homomorphism(SM, •M, idM) (SN, •N, idN)
MonCategory of monoids
Page 120
M H NMonoid Monoid
Monoid homomorphism(SM, •M, idM) (SN, •N, idN)
MonCategory of monoids
Objects = monoidsArrows = monoid homomorphismsComposition = function compositionIdentity = Identity function
Page 121
M H N
SM SN
“structure-preserving map”
Set Setfunction
hSets
Where h preserves composition & identity
Page 122
Example
String length is a monoid homomorphism from (String, +, "") to (Int, +, 0)
Page 123
Preserves identity
Preserves composition
"".length == 0
(str1 + str2).length = str1.length + str2.length
Page 124
Monoids in codetrait Monoid[M] { def compose(a: M, b: M): M def id: M}
Page 125
Monoids in codedef foldMonoid[M: Monoid]( ms: Seq[M]): M = { ms.foldLeft(Monoid[M].id) (Monoid[M].compose)}
Page 126
Int / 0 / +import IntAddMonoid._foldMonoid[Int](Seq( 1,2,3,4,5,6))
21
Page 127
Int / 1 / *import IntMultMonoid._foldMonoid[Int](Seq( 1,2,3,4,5,6))
720
Page 128
String / "" / +foldMonoid[String](Seq( "alea", "iacta", "est"))
”aleaiactaest"
Page 129
Endos / id / ∘def mash: Potato => Potatodef addOne: Int => Intdef flipHorizontal: Shape => Shapedef bestFriend: Person => Person
Page 130
A=>A / a=>a / composefoldMonoid[Int => Int](Seq( _ + 12, _ * 2, _ - 3))
(n: Int) => ((n + 12) * 2) - 3
Page 131
Are chairs monoids?
Page 132
ChairComposition = You can’t turn two chairs into oneIdentity =
Page 134
Chair stackComposition = stack them on top Identity = no chairs
Page 135
Chair Stack is the free monoid of chairs
Protip: just take 0-to-many of anything, and you get a monoid for free
Page 136
…almost
Real monoids don’t topple; they keep scaling
Page 137
Monoids embody the principle of weakly-typed composability
Page 138
IV. Products & sums
Page 139
Algebraic Data TypesList[A] - Cons(A, List[A]) - Nil
Option[A] - Some(A) - None
BusinessResult[A] - OK(A) - Error
Wiggles - YellowWiggle - BlueWiggle - RedWiggle - PurpleWiggle
Address(Street, Suburb, Postcode, State)
Page 140
Algebraic Data Types Cons(A × List[A]) + Nil
Some(A) + None
OK(A) + Error
YellowWiggle + BlueWiggle + RedWiggle + PurpleWiggle
Street × Suburb × Postcode × State
Page 141
Algebraic Data Types
A × List[A] + 1 A + 1
A + 14
Street × Suburb × Postcode × State
Page 142
Algebraic Data Types
A × List[A] + 1 A + 1
A + 14
Street × Suburb × Postcode × State
isomorphic
Page 143
Terminology
isomorphism
Page 144
Terminology
isomorphismEqual
Page 145
Terminology
isomorphismEqual-shape-
ism
Page 146
Terminology
isomorphism“Sorta kinda the same-ish” but I want to sound really smart -
Programmers
Page 147
Terminology
isomorphism“Sorta kinda the same-ish” but I want to sound really smart -
Programmers
Page 148
Terminology
isomorphismOne-to-one mapping between two objects so you can go back-and-forth without losing information
Page 149
Isomorphism
object objectarrows
Page 150
Isomorphism
Same as identity
Page 151
Isomorphism
Same as identity
Page 152
These 4 Shapes
Wiggles
Set functionsSet
Page 153
These 4 Shapes
Wiggles
Page 154
These 4 Shapes
Wiggles
Page 155
There can be lots of isos between two objects!
If there’s at least one, we can say they are isomorphic
or A ≅ B
Page 156
Products
A × BA Bfirst seco
nd
Given the product of A-and-B, we can obtain both A and B
Page 157
Sums
A + BA Bleft right
Given an A, or a B, we have the sum A-or-B
Page 158
Opposite categoriesC Cop
A
B
C
g ∘ f
f
g
A
B
C
fop ∘ gop
fop
gop
Isomorphic!
Page 159
A
B
C
g ∘ f
f
g
A
B
C
f ∘ g
f
g
Just flip the arrows, and reverse composition!
Page 160
A
A×B
B
A product in C is a sum in CopA sum in C is a product in Cop
A+B
B
A
C Cop
Page 161
Sums ≅ Products!
Page 162
Terminology
dual
An object and its equivalent in the opposite category are
to each other.
Page 163
Terminology
Co-(thing)
Often we call something’s dual a
Page 164
Terminology
Coproducts
Sums are also called
Page 165
V. Composable systems
Page 166
Growing a system
Banana
Page 167
Growing a system
Page 168
Growing a system
Page 169
Growing a system
Bunch
Page 170
Growing a system
Bunch Bunch
Page 171
Growing a system
Bunch BunchBunch
Page 172
Growing a system
Bunch BunchBunch
BunchManager
Page 173
Growing a system
Bunch BunchBunch
BunchManagerAnyManagers
Page 181
Using composable abstractions means your code can grow without getting more complex
Categories and Monoids capture the essence of composition in software!
Page 182
Look for Monoids and Categories in your domain where you can
You can even bludgeon non-composable things into free monoids and free categories
Page 185
SpannerAbstractSpanne
r
Page 186
SpannerAbstractSpanne
r
AbstractToolThing
Page 187
SpannerAbstractSpanne
r
AbstractToolThingGenerallyUsefulThing
Page 188
SpannerAbstractSpanne
r
AbstractToolThingGenerallyUsefulThing
AbstractGenerallyUsefulThingFactory
Page 189
SpannerAbstractSpanne
r
AbstractToolThingGenerallyUsefulThing
AbstractGenerallyUsefulThingFactory
WhateverFactoryBuilder
Page 191
That’s not what abstraction means.
Page 192
Code shouldn’t know things that aren’t needed.
Page 193
def getNames(users: List[User]): List[Name] = { users.map(_.name)}
Page 194
def getNames(users: List[User]): List[Name] = { println(users.length) users.map(_.name)}
Over time…
Page 195
def getNames(users: List[User]): List[Name] = { println(users.length) if (users.length == 1) { s”${users.head.name} the one and only" } else { users.map(_.name) }}
Page 196
“Oh, now we need the roster of names! A simple list won’t do.”
Page 197
def getRosterNames(users: Roster[User]): Roster[Name] = { users.map(_.name)}
Page 198
def getRosterNames(users: Roster[User]): Roster[Name] = { LogFactory.getLogger.info(s”When you party with ${users.rosterTitle}, you must party hard!") users.map(_.name)}
Over time…
Page 199
def getRosterNames(users: Roster[User]): Roster[Name] = { LogFactory.getLogger.info(s"When you party with ${users.rosterTitle}, you must party hard!") if (users.isFull) EmptyRoster("(everyone) ") else users.map(_.name)}
Page 200
When code knows too much, soon new things will appear that actually require the other stuff.
Page 201
Coupling has increased. The mixed concerns will tangle and snarl.
Page 202
Code is rewritten each time for trivially different requirements
Page 203
def getNames[F: Functor](users: F[User]): F[Name] = { Functor[F].map(users)(_.name)}
getNames(List(alice, bob, carol))getNames(Roster(alice, bob, carol))
Page 204
Not only is the abstract code not weighed down with useless junk, it can’t be!
Reusable out of the box!
Page 205
Abstraction is about hiding unnecessary information. This a good thing.We actually know more about what the
code does, because we have stronger guarantees!
Page 206
We’ve seen deep underlying patterns beneath superficially different things
A×B A+B
Page 207
Just about everything ended up being in a category, or being one.
Page 208
There is no better way to understand the patterns underlying software than studying Category Theory.
Page 209
Further readingAwodey, “Category Theory”Lawvere & Schanuel, “Conceptual Mathematics: an introduction to categories”Jeremy Kun, “Math ∩ Programming” at http://jeremykun.com/Gabriel Gonzalez “Haskell for all”
http://www.haskellforall.com/2012/08/the-category-design-pattern.htmlhttp://www.haskellforall.com/2014/04/scalable-program-architectures.html