105
Category Theory for beginners YOW Lambda Jam 2015 @ KenScambler A B C

Category Theory for beginners - YOW! Conferences …yowconference.com.au/slides/yowlambdajam2015/Scambler...Category Theory for beginners YOW Lambda Jam 2015 @KenScambler A B C Functions

Embed Size (px)

Citation preview

Category Theory for

beginnersYOW Lambda Jam 2015

@KenScambler

A B

C

Functions

They must be good; we even have conferences for them

A B

Set

f

functionSet

A B

elements of A

elements of B

f

Mappings from

individual A elements

to B elements

We like functions.

They’re simple.

They compose.

A B

f

C

g

A C

g ∘ f

What if we keep

the composition,

and throw away

sets & functions?

A

Something

f

Some composable relation

between A & B Something

B

Categories

It turns out that you

can describe most of

maths, just from this

starting point!

Dizzyingly abstract

Categories appear at every

step of the abstraction

continuum

For pragmatic real-world

programmers with deadlines?

Couple of things.

Couple of things.

Programming = maths

Couple of things.

Programming = maths

Programming = abstraction

Perhaps categories

have something to

offer us after all!

Category

Objects1.

Category

Objects

Arrows or morphisms2.

Category

Objects

Arrows

Domain f

dom(f)

3.

Category

Objects

Arrows

Domain/Codomain f

cod(f)

dom(f)

4.

Category

Objects

Arrows

Domain/Codomain

dom(g)

cod(g)

g4.

Category

Objects

Arrows

Domain/Codomain4.

Category

Objects

Arrows

Domain/Codomain

Composition

f

5.

Category

Objects

Arrows

Domain/Codomain

Composition

fg

5.

Category

Objects

Arrows

Domain/Codomain

Composition

fg

g ∘ f

5.

Category

Objects

Arrows

Domain/Codomain

Composition

f

5.

Category

Objects

Arrows

Domain/Codomain

Composition

f

h5.

Category

Objects

Arrows

Domain/Codomain

Composition

f

h

h ∘ f

5.

Category

Objects

Arrows

Domain/Codomain

Composition

Identity6.

Category

Compose

∘ : (B C, A B) => (A C)

Identity

id : A A

Category Laws

Associative Law

(f ∘ g) ∘ h = f ∘ (g ∘ h )

Identity Laws

f ∘ id = id ∘ f = f

Associative law

(f ∘ g) ∘ h = f ∘ (g ∘ h )

f

g

h

(g ∘ h)

(f ∘ g)

Associative law

(f ∘ g) ∘ h = f ∘ (g ∘ h )

f

g

h

(g ∘ h)

(f ∘ g)

Associative law

(f ∘ g) ∘ h = f ∘ (g ∘ h )

f

g

h

(g ∘ h)

(f ∘ g)

Associative law

(f ∘ g) ∘ h = f ∘ (g ∘ h )

f

g

h

(g ∘ h)

(f ∘ g)

Associative law

(f ∘ g) ∘ h = f ∘ (g ∘ h )

f

g

h

(g ∘ h)

(f ∘ g)

Associative law

(f ∘ g) ∘ h = f ∘ (g ∘ h )

f

g

h

(g ∘ h)

(f ∘ g)

Associative law

(f ∘ g) ∘ h = f ∘ (g ∘ h )

f

g

h

(g ∘ h)

(f ∘ g)

Identity laws

f ∘ id = id ∘ f = f

fid id

Identity laws

f ∘ id = id ∘ f = f

fid id

Identity laws

f ∘ id = id ∘ f = f

fid id

Identity laws

f ∘ id = id ∘ f = f

fid id

Sets & functions

Person

String Integer

bestFriend

length

name age

+1

(not identity!)

(not identity!)

Sets & functions

Infinite arrows from composition

+1∘ length ∘ name

bestFriend ∘ bestFriend

bestFriend ∘ bestFriend ∘ bestFriend

+1∘ age∘ bestFriend

Sets & functions

Objects

Arrows

Composition

Identity

Sets & functions

Objects = sets (or types)

Arrows = functions

Composition = function composition

Identity = identity function

Categories in code

trait Category[Arr[_,_]] {

def compose[A,B,C](c: Arr[B,C], d: Arr[A,B]): Arr[A,C]

def id[A]: Arr[A,A]

}

Category of Types & Functions

object FnCatextends 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)}

Category of Garden Hoses

sealed trait Hose[In, Out] {def leaks: Intdef kinked: Boolean

def >>[A](in: Hose[A, In]):Hose[A, Out]

def <<[A](out: Hose[Out, A]):Hose[In, A]

}

Category of Garden Hoses

object HoseCatextends Category[Hose] {

def compose[A,B,C](c: Hose[B,C], d: Hose[A,B]): Hose[A,C] = {

c << d }

def id[A]: Hose[A,A] = EmptyHose

}

Categories embody the

principle of

strongly-typed

composability

Functors

Functors

Functors map between categories

Objects objects

Arrows arrows

Preserves composition & identity

CF

DCategory Category

Functor

CF

DCategory Category

Functor

CatCategory of categories

CF

DCategory Category

Functor

CatCategory of categories

Objects = categories

Arrows = functors

Composition = functor composition

Identity = Identity functor

Functors in code

trait Functor[F[_]] {

def map[A,B](fa: F[A],

f: A => B): F[B]

}

Functors in code

trait Functor[F[_]] {

def map[A,B](fa: F[A],

f: A => B): F[B]

}

Objects to objects

Functors in code

trait Functor[F[_]] {

def map[A,B](fa: F[A],

f: A => B): F[B]

}

Arrows to arrows

Functors in code

class Functor f where

fmap :: (a b) (f a f b)

Arrows to arrows

Composable systems

Growing a system

Banana

Growing a system

Growing a system

Growing a system

Bunch

Growing a system

BunchBunch

Growing a system

BunchBunch

Bunch

Growing a system

BunchBunch

Bunch

BunchManager

Growing a system

BunchBunch

Bunch

BunchManager

AnyManagers

compose

compose

etc…

Using composable abstractions

means your code can grow

without getting more complex

Categories capture the essence

of composition in software!

Look for Categories (such

as Monoids) in your

domain where you can

You can go out of your way to

bludgeon non-composable

things into a category

Abstraction

Spanner

Spanner

AbstractSpanner

Spanner

AbstractSpanner

AbstractToolThing

Spanner

AbstractSpanner

AbstractToolThing

GenerallyUsefulThing

Spanner

AbstractSpanner

AbstractToolThing

GenerallyUsefulThing

AbstractGenerallyUsefulThingFactory

Spanner

AbstractSpanner

AbstractToolThing

GenerallyUsefulThing

AbstractGenerallyUsefulThingFactory

WhateverFactoryBuilder

That’s not what

abstraction means.

Code shouldn’t know

things that aren’t needed.

def getNames(users: List[User]): List[Name] = {

users.map(_.name)}

def getNames(users: List[User]): List[Name] = {

println(users.length) users.map(_.name)

}

Over time…

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)

}}

“Oh, now we need

the roster of names!

A simple list won’t

do.”

def getRosterNames(users: Roster[User]): Roster[Name] = {

users.map(_.name)}

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…

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)}

When code knows too much,

soon new things will appear that

actually require the other stuff.

Coupling has increased.

The mixed concerns will

tangle and snarl.

Code is rewritten each time for

trivially different requirements

def getNames[F: Functor](users: F[User]): F[Name] = {

Functor[F].map(users)(_.name)}

getNames(List(alice, bob, carol))

getNames(Roster(alice, bob, carol))

Not only is the abstract code

not weighed down with

useless junk, it can’t be!

Reusable out of the box!

Abstraction is

about hiding

unnecessary

information. This

a good thing.

We actually know more about what the code

does, because we have stronger guarantees!!

Categories show deep

underlying patterns

beneath superficially

different things

Set Set

function

Cat Cat

functor

Just about everything

ends up being in a

category, or being one.

There is no better

way to understand

the patterns

underlying software

than studying

Category Theory.