Reading Viktor's post, I found myself provoked by some – in my
opinion – omissions and inaccuracies in his definitions.
He also has a different perspective on how categories apply to
programming and to Haskell in particular, to which I tend to
partially disagree.

Instead of, as I originally intended, just commenting on specific
topics, I set about describing what functors and related concepts are
in category theory, how they map to Haskell, and how they are being
used in that language.  The resulting text is longer than I would
like it, but, hopefully, it clarifies the topic with sufficient
detail and so can be useful in some ways to J-ers.

Categories and functors
***********************

A category consists of two kinds of entities: objects and morphisms,
a.k.a. arrows.  Each arrow maps one single object to another (in the
same category), and is what we call a function in other contexts.
Arrows can be composed one after another, thus producing new arrows.

The arrows of a category furnish it with structure by relating
its objects to each other.  Arrows, in turn, are related through
composition, so the latter plays an important role in respect of
the category's structure.

It is often the case that the objects and arrows of a category come
from some other domain of consideration. However, from the perspective
of the category, neither of them has an internal structure or whatever
properties of their own.  Being the domain or codomain of arrows is
the only thing at all that an object can be in its category, and an
arrow is nothing more than a link from one object to another.

A functor is a mapping from one category, A, to another (or sometimes
the same), B.  It consists of two maps – one from the objects of A to
those of B, and another from the arrows of A to those of B.  Now, we
expect a functor to not just be an arbitrary mapping, but also to
correlate the structure of its domain category (A) to that of the
codomain (B).  To this end, the arrow part of the functor, let's call
it Fa, has to obey, for each pair of arrows f and g in A, the identity

        Fa (f∘g) ≡ (Fa f) ∘ (Fa g)

which means that the image in B of the composition f∘g in A is
precisely the composition of the images of f and g (the latter
composition taking place in B).  Or, shortly put: the image of a
composition is the composition of images.  We could also say that
'Fa distributes over ∘', in the same sense that multiplication
distributes over addition in arithmetic.

There is one more property for Fa to obey – it is so simple and
obviously necessary that it is easy to remain unnoticed, but it
has to be stated explicitly: Fa has to also 'distribute' over the
identity arrows: if i(x) is the arrow from x to itself in A, then

        Fa i(x) ≡ i(Fa x) ,

where i(Fa x) is the arrow from Fa x to itself.

In the sense of the above two axioms, a functor is a structure-
preserving operation on categories.

Categories and functors in Haskell
**********************************

In Haskell, the concept of a functor is very close to the one in the
category theory.  Of course, this is an empty statement unless we know
what in Haskell corresponds to categories, objects, and arrows.  So,
first things first.

Think of datatypes as objects of which categories are constructed.
We can imagine one universal category whose objects are all the
datatypes of a programming language (here, Haskell).  Correspondingly,
each function is an arrow in this category.

Data structures, such as lists, trees etc., are usually represented
as datatypes.  But, in a typed language, one cannot have a data object
that is simply 'a list', there must be specified 'a list of integers'
or similar.  So, from each data structure emerges a family of concrete
types with the same organization but different types of elements.  It
makes sense to think of such a family as parameterized by its element
type.  It is therefore useful to associate with the type family a
function which, given a datatype, produces another type – a member of
the family.  In Haskell, this concept is embodied in the so called
'type constructors', or 'tycons' for short.

From a categorial point of view, to each family of types as described
above, there corresponds a category (whose objects are the members of
the family).  Thus there are categories of lists, of trees, etc.
The objects of the category of lists are the types of integer-valued,
real-valued, string-valued, etc. lists.

Note that there is a one-to-one correspondence between tyconses and
categories (except the universal category), naturally established
through both being associated with the same family of types.

As any other value, functions are typed, and their types are written
f :: a -> b, meaning that the function f has a domain a and a codomain
b.  Because there can be many functions of the same signature  a -> b,
there are many arrows between any two objects in any of the categories,
the universal one or the specific.

After all this, what is a functor in Haskell?  It is a mapping, in
the above described categorial sense of the word 'functor', from the
universal category of types to any one of the specific categories.
The definition of functor is embodied in the following type class
interface:

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

together with the axioms

        fmap id ≡ id
        fmap (f . g) ≡ fmap f . fmap g

In the above interface, t represents a dummy tycon for which a
function fmap is defined that has the given signature and satisfies
the axioms.  fmap transforms any function into a function whose domain
and codomain are types produced by that constructor.  (Given that the
original function's domain and codomain are a and b, fmap produces a
function whose domain and codomain are the types (t a) and (t b).)
In this way, fmap 'adapts' general functions for use within the data
structure represented by the tycon.

E.g., the t can be the tycon of lists which, given a type x, produces
a list with element type x.  That t is made an instance of Functor by
trivially defining fmap to be the library function 'map' which, given
a function f, creates a function that transforms a list by applying f
to each of its elements.

A tycon for which fmap exists is often, informally, called a functor.
Strictly speaking, it implements a functor with codomain the tycon's
respective category.

Note that fmap only represents the arrow part of the functor.  The
object part is implicit.  It must also be stressed that satisfying
the axioms is not (and could not be) automatic – ensuring that they
are valid for a specific implementation of fmap is a programmer's
obligation!

Functors in J?
**************

The observed correspondence between category theory and Haskell can
be summarized in the following table.

category theory           Haskell
---------------------------------------------------------------------
category                  the universal category of types;
                          specific categories (families of datatypes)

category constructor      type constructor

object                    type

arrow                     function

composition of arrows     composition of functions

definition of functor     Functor interface and axioms
as concept

functor                   axioms-complying implementation of Functor
                          (associated with a specific tycon)

If we can build a similar table with J in the right column, we can
say that we have an idea what might be a J's version of functors in
general.
----------------------------------------------------------------------
For information about J forums see http://www.jsoftware.com/forums.htm

Reply via email to