Banner

Category theory and Haskell : Part 2

Posted by alpheccar - Jun 24 2007 at 21:48 CEST

In my previous post, I explained that with category theory you can define some concepts in such a way that they can be used in several different contexts. As a side effect, the definitions are rather abstract since they are forbidden from talking about the implementation of the objects and must rely only on the provided interfaces.

So, a first thing to do when studying category theory is learning some of these definitions. Some of them are just generalizations of ideas commonly used in set theory. I introduced a few of them in my previous post but I used a terminology that is not standard.

So, before continuing and introducing some additional definitions, let me complete my previous post and give the standard terminology that I will now use.

Standard terminology

  • Processes are named arrows. So f :: A -> B is an arrow from the domain A to the codomain B
  • The "do nothing process" is noted id for identity
  • The set of arrows from A to B is noted hom(A,B) (hom for homomorphism)
  • To ease the experiments with haskell I will sometimes write f :: a -> b. But, Category Theory is not polymorphic. So, the definitions given are for a fixed domain A and codomain B
  • A terminal object (playing the role of a singleton) is generally noted 1
  • The category of Haskell programs and types is named Hask

Now, let's see some additional definitions.

Product

A product of two sets A and B (A x B) is easy to define when you can speak of the elements of a set. But, how can you define it when there are no elements ?

The Haskell pair type is nearly a product. So, let's assume it is one and we will see after why it is not really one. Assuming for a moment that the pair type is a product is useful to motivate the definition for a product.

The Haskell pair type is :

Haskell Code by HsColour
(A,B)

That type is useful in Haskell because you have the possibility to extract the left or right values of a pair. You have functions:

Haskell Code by HsColour
fst :: (A,B) -> A
snd :: (A,B) -> B

Thanks to fst and snd and the pair type, you can replace any pair of functions from the same type C with an unique function h:

Haskell Code by HsColour
f :: C -> A
g :: C -> B

delta :: (C -> A) -> (C -> B) -> (C -> (A,B))
delta f g = \x -> (f x, g x)

h :: C -> (A,B)
h = delta f g

f is fst . h and g is snd . h

So, the pair is giving a way to replace any pair of functions by a function to A x B. It is the caracteristic feature of this pair type and it is the definition chosen by the math guys for the categorical product.

So, the product AxB of objects A and B in any category (when it exists) is an object with the property that each time you have arrows C -> A and C -> B you can replace them by an arrow C -> AxB.

Category theory definition are clearer when you use a diagram.

image

The previous diagram is commutative. Any paths starting at the same object and ending at the same object , must be equals. So we have for instance f = fst . h and g = snd . h.

Saying that AxB is a cartesian product means that any object C with arrows to A and B has an arrow to AxB such that the corresponding diagram is commutative. AxB is the last object with arrows to A and B since any other objects with arrows to A and B is also pointing to AxB.

Of course, AxB is defined up to isomorphism. I am not saying that the object is unique but if there is another one you cannot distinguish it since it will behave in exactly the same way.

Unfortunately, the pair type is not the categorical product because undefined is contained in any pair type and is different from (undefined,undefined). So, the previous diagram should not contain (A,B) which is not the categorical product A x B.

Here is a counterexample showing why (A,B) is not a categorical product:

Haskell Code by HsColour
data A
data B
data C

instance Show A where
    show _ = "A"
    
instance Show B where
    show _ = "B"
        
instance Show C where
    show _ = "C"

f :: C -> A
f _ = undefined

g :: C -> B
g _ = undefined

h :: C -> (A,B)
h _ = (undefined,undefined)

h' :: C -> (A,B)
h' _ = undefined

We have f = fst . h and f = fst . h' and g = snd . h and g = snd . h'. The pair of arrows f,g to A and B is not uniquely determining an arrow h to (A,B) since h' is also a possibility. If there is no unicity the pair is not a categorical product in Hask.

Coproduct

If you reverse the direction of arrows in the product definition you get the definition of a coproduct.

image

In Haskell, the coproduct of A and B may be defined with an union type (no recursion in the type):

Haskell Code by HsColour
data C a b = InLeft a | InRight b

In category theory it is written : A + B

It is not really a coproduct because of problems related to undefined. But, let's assume it is a coproduct to explain with some Haskell code the meaning of the coproduct definition.

Since we reversed the direction of arrows to get the coproduct, the fst and snd functions used to project AxB on A and B are now replaced by functions from A to A+B and B to A+B. These functions are just:

Haskell Code by HsColour
InLeft :: a -> C a b
InRight :: b -> C a b

The meaning of the coproduct is : each time you have arrows from A to C and B to C then you can replace them by an arrow from A+B to C.

Haskell Code by HsColour
f :: a -> c
g :: b -> c

h :: (a -> c) -> (b -> c) -> (C a b -> c)
h f g = \x -> case x of
 InLeft a -> f a
 InRight b -> g b

This function is nothing more than the Haskell either function.

The coproduct is the first object with arrows from a and b since any other object with arrows from a and b has an arrow from the coproduct.

With product and coproduct we can write any Haskell data type in a different way. For instance,

Haskell Code by HsColour
data C a = Z (Int,a) | B

can be written a x Int + 1

a x Int is not difficult to understand since we have a pair.

+ is the choice between Z or B. But, why is B replaced by 1 ? Because there is only one value equal to B. But I can't define a type containing just that value.

Haskell Code by HsColour
data B = B 

is containing B of type B and undefined of type B. So, I cannot create this data C from a type (Int,A) and a type B. But, if it was possible then that type B having only one value would be a terminal object that is generally noted 1 in category theory since it is playing the role of a singleton.

If I am insisting on other ways to write the data type it is because it will be useful when I introduce functors and zippers later.

Equalizer

An equalizer is another very useful categorical definition. It is given by the following diagram. An equalizer is an object E and and arrow e such that:

Haskell Code by HsColour
f . e = g . e

and any other arrow h such that

Haskell Code by HsColour
f . h = g . h

is in fact given by an arrow to E.

image

So, an equalizer is the most general solution to an equation f = g.

In Hask it is not always possible to find an equalizer.

Functor

I will write a lot about functors in my next post. They are key to define algebras, coalgebras, monads etc... Here, I am just giving the definition and the fun will really start with the next post.

A functor is a magical tool allowing to travel from one universe (a category) to another universe.

For instance, we may have a functor from Set to Hask or another one from Hask to Ocaml or ...

A functor is thus the specification of a transformation on the objects and on the arrows.

If:

Haskell Code by HsColour
f :: A -> B
g :: B -> C

are arrows and objects in a category X, then F is a functor from X to Y if

Haskell Code by HsColour
F(A) and F(B) and F(C)

are objects in a category Y ;

Haskell Code by HsColour
F(g . f) = F(g) . F(f)

and

Haskell Code by HsColour
F(id_A) =id_F(A)

The identity arrow from A to A is transformed into the identity arrow from F(A) to F(A) for each object A.

Note that in the given definition nothing prevents X and Y from being equal.

list is a functor from Hask to Hask with an action on types and on Haskell functions. We will see it next time what it means.

Tags | |

Comments

Add a comment...

Posted by alpheccar - Jul 02 2007 at23:37 CEST

If the equalizer exists (it depends on f and g and the category) then u is always existing for any h.

(T,h) is a possible solution. There may not be any other solution than (E,e) (which means than any other solution will be isomorphic to E so u will be an iso).

Ahh ... I get it

Posted by Justin - Jul 02 2007 at23:16 CEST

Thanks for your reply regarding equalizer. That makes a lot of sense. So "u" is a process that can take us from T to E in the diagram, and then 'e' takes us to A. Similarly, 'h' takes us directly from T to A.

I'm curious - can T be any category? When is there no 'u' to E?

Posted by none - Jun 30 2007 at08:13 CEST

The Wikibooks article

http://en.wikibooks.org/wiki/Haskell/Category_theory

explains category theory as applied to Haskell types and monads pretty well. It's not quite as abstract as this article so it makes a nice companion to it.

Posted by alpheccar - Jun 27 2007 at18:40 CEST

Derek : I know this book. It looks good but I have not read it so far.

Justin : you're right to ask. I should have insisted on one important point. In Category theory, when you give a definition you're generally defining an universal object. Roughly, it means that it is the most general object with a given property and any other object with a similar property will be related to that universal object in an unique way.

For instance, any object with a pair of arrows to A and B is related to the product AxB in an unique way with an arrow. It is the meaning of a categorical product.

For an equalizer it is the same. Th equalizer (object E and arrow e) is the most general solution to the equation f = g. So any other solution must be related to the most general one (and be less general).

So, each time you have a solution h such that f . h = g . h then you have an unique u such that h = e . u.

So u is determined uniquely from h because (E,e) is universal. It means that h is less general than e and the less general is encoded with u.

So, when in Category theory you see a diagram for the definition of a concept then it means that a part of the diagram is uniquely determined from other parts of the diagram.

And the diagram being commutative, you can read lot of equations from it. All paths starting at a same object and ending at same other object are equal.

So, for the equalizer, just from the diagram you have for instance:

f . e = h . e because both paths are starting at E and ending at B.

Computational Category Theory

Posted by Derek Elkins - Jun 27 2007 at04:19 CEST

Have you looked at Rod Burstall's book (available online with a bit of digging) titled "Computational Category Theory"?

Equalizer?

Posted by Justin - Jun 27 2007 at01:45 CEST

I'm following so far, and really like these posts. Thanks for putting them up.

I was a bit confused on what an Equalizer is and what it does. What does the "u" arrow do in the diagram? Maybe you can write a little more about them if they are important?

Thanks

Posted by alpheccar - Jun 24 2007 at23:19 CEST

There are probably other typos. I'll read this post again tomorrow to correct them. I was a bit short on time tonight.

Posted by Pied - Jun 24 2007 at23:03 CEST

I didn't read it fully yet though there's a little type at the end :

>list is a functor from Hask to Hask with and action on types and on Haskell functions.

you meant "with an action" I guess.

P!