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:
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
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.
We need to map all of the source category functions, including compositions.
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 fmap
ing 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 fmap
ing 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