Functoriality


I’ve been trying to learn Category theory.

More realistically, i want to better understand the functional programming paradigm.

Functors in category theory are fascinating, as they generally behave like functions, but at a higher level of abstraction.

Functors?

What is a Functor?

According to the Wiki:

a functor is a mapping between categories

Here is a very simplified diagram:

F

D

F f

Fa

Fb

C

f

a

b

Note that we map the objects in C to objects in D, and we map the morphisms (read: functions) in C, to morphisms in D as well!

The Functor F maps the entire category.

In Haskell, this is a “function between types”. Here is the definition:

class Functor f where
  fmap :: (a -> b) -> f a -> f b

So What?

Here are the useful properties:

  • Functors preserve identity
  • Functors preserve composition

Identity

If we consider identity:

id x = x

F

D

idFa

Fa

C

ida

a

It should make sense that, formally:

F ida == idFa for every object a in C

If we think of the identity function as “multiplying by one”, then it doesnt matter if we multiply by one before or after applying a function.

In Haskell we can say:

fmap id = id

Composition

Consider now composition of functions in the source category.

C

f

g

g ∘ f

a

b

c

We need to map all of the source category functions, including compositions.

F

D

F f

F g

F (g ∘ f)

Fa

Fb

Fc

C

f

g

g ∘ f

a

b

c

The strong condition here, and important definition as to whether this is a proper Functor, is that we need:

F g ∘ F f == F (g ∘ f)

Composing the Functor is the exact same as the Functor of the composition!

Formally:

F g ∘ F f == F (g ∘ f) for all morphisms f : a → b and g : b → c in C.

A Note

Not all mappings between categories are Functors.

We can write mappings that take identity functions to completely different functions.

Or perhaps composition “breaks” by, for example, collapsing multiple inputs to a single value.

But if a mapping follows these definitions, it is a Functor, and if a mapping is a Functor, then it follows these definitions.

Lets try it out

Consider the (possibly) simplest algebraic data type, Maybe. It is a functor:

data Maybe a = Nothing | Just a

A note, remember that Functors can fmap, which is part of the “shape” of a Functor (it’s interface).

Maybe?

A quick primer:

i = Just 2
noti = Nothing

The rules

Identity

Lets check identity. The identity function is:

id :: a -> a
id a = a

Maybe values preserve identity:

id i = Just 2
id noti = Nothing

Does fmaping over Maybe values also preserve identity?

fmap id i = Just 2
fmap id noti = Nothing

It does.

Composition

Consider some function, that adds two to it’s argument:

f x = x + 2

As expected:

f 2 = 4

Lets try fmaping over this function:

fmap f i = Just 4
fmap f noti = Nothing

Works a treat! But that’s just the definition of fmap, applied.

We can compose functions:

(f . f) 2 = 6

How about if we fmap over composing functions?

fmap (f . f) i = Just 6
fmap (f . f) noti = Nothing

The very interesting bit is that composing the maps is the exact same thing, mathematically!

(fmap f . fmap f) i = Just 6
(fmap f . fmap f) noti = Nothing

Take aways

Functors are the abstract pattern behind higher order functions (functions over functions) and we can use them productively in our programs.

Not all languages even have first-class functions, and not all of those languages natively support Functors.

But in a language that does, these are a simple, yet very powerful concept.

Learn you a Haskell for great good!

… … in the category of endofunctors

Share: