First caution: the forum archived copy of this message
(http://jsoftware.com/pipermail/programming/2012-April/027795.html)
was arbitrarily truncated by some mail handling software.  The first
complete instance of the text seems to be the response at
http://jsoftware.com/pipermail/programming/2012-April/027804.html

Second caution: I think that this is a very solid effort, for
explaining the issues relevant to understanding functors in the
context of J.  I do not recall seeing better.  However, I have some
difficulties with it, which may be difficulties with the underlying
concepts.  No one should take my having these difficulties as a
personal affront.

I am going to annotate Boyko's original text with some questions, and
then I will come back to my understandings of the related issues after
his text.

On Sun, Apr 1, 2012 at 5:41 PM, Boyko Bantchev <boyk...@gmail.com> wrote:
> 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.

Q1: What is an object?
Q2: What does this "single object" constraint mean?
Q3: (only relevant in the context of the above two questions): What is
a function or arrow or morphism, here?

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

Q4: is the above saying that a category can be an object?  If so, what
does that tell us when combined with our answers to my previous
questions.  (This question is not interesting to me without those
answers.)

> 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.

I note here that we are using the concept of "an image in a domain".
This tells us a part of the answer for Q1.  It also raises some other
questions for me.

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

Q5: In the opening paragraphs, arrows were defined between two
different objects.  Do we have here a requirement for arrows that map
from an object to itself?  Also, what does it mean if we have an arrow
from x to x and an arrow from Fa x to Fa x and they are "the same
arrow"?  [This is probably just an english grammar issue.]

> 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.

Q6: A type family has an arbitrarily large number of members (and this
number can probably be infinite if we are talking about math instead
of Haskell).  Does it matter which member of the family we produce in
a tycons?

> 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: partial answer to Q1 here.

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

Note: this part of the definition of fmap looks very much like the J
concept of an adverb.  (But it could also be a verb that accepts a
gerund argument and produces a gerund result, for the case where the
gerunds we are considering in both the verb's domain and codomain each
represent only one verb.)

> together with the axioms
>
>        fmap id ≡ id
>        fmap (f . g) ≡ fmap f . fmap g

And this part of the definition of fmap tells us that only some
adverbs (or verbs) can be valid fmaps.

> 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!

This stressed aspect fits J well enough:  It's up to the programmer to
ensure that the adverb (or whatever) being defined is a valid fmap.

> 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.

So... back to my questions:

Q1:  "what is an object"?

We know that an object is something that in some way corresponds to a
function's argument, or its domain.  So, I might have a function which
maps from R3 to R3 (R3 being: three independent real numbers).  Is R3
an object?  I might instead have a function which maps from Z3 to Z3
(Z3 being: three independent complex numbers), or I3 to I3.  Is this
the same function, or do we have to have a different function every
time we have a different domain?

Let's consider some example functions:

F1:  result is always the single integer 0 regardless of its argument.
F2:  1 + its argument
F3:  the largest integer N where N is strictly less than its argument


F1 can be made to work with any domain bit has a restricted codomain
F2 has a domain which can only be numeric and probably has no identity element
F3 has a restricted domain (I think it cannot include sequences of numbers)

In other words:
F1(1.5) would be 0
F1(1j1) would be 0
F1('this is a test') would be 0

F2(1.5) would be 2.5
F2(1j1) would be 2j1
F1('this is a test') would not be valid

F3(1.5) would be 1
F3(1j1) would not be valid
F3('this is a test') would not be valid

So what are the objects here?

If I say that the objects are the domains, then it's clear
that each of these functions have different domains.  But
this creates a problem for me:  F1 is valid for the domain
of F3.  How am I to understand this in the context of "Each
arrow maps one single object to another (in the same category)"

Alternatively, I could say that each argument and each result is an
object.  So, 1 is an object, 2 is an object, 1j1 is an object, 1 2 3
is an object, and so on.  One variation of this approach is that I
have an infinity of arrows.  The arrow from 1->2 is different from the
arrow from 2->3.  But this means that F2 is not an arrow but a
potentially infinite collection of arrows.

Another approach to take is that arrows connect many objects and the
"single object" constraint means that each object in the domain
connects to only one object in the codomain.  I think that this
variation best fits the description here.

Put differently: a domain is a set (as is a codomain), and an object
is a member of that set, and an arrow is a function on members of the
set.  Domains can easily be infinitely large (in math) or can have
members which nearly fill a computer's memory (in programming).  If we
are dealing with arbitrary functions, each function can have a domain
which does not match that of another function.  And, distinct domains
can have non-empty intersections.

Q2 -- in the point of view I have expressed here, the answer to Q2 is
that arrows are functions and not more general relationships.

Q3 has no special answer here.  A function is just a function -- all
the complexity has gone into the definition of the functions' domains
and the definitions of the individual functions.  (This would have
been more complicated if we said that a function was the thing that
mapped a single object to a single object -- that each such mapping
was a different function.)

Q4 from my current viewpoint: a category need not be an object but
could be a (potentially infinite) collection of objects.  But it could
be an object also.  An infinite set can be an object, so it stands to
reason that size has nothing to do with "objectness".  So this was not
an interesting question.

Q5: No: We do not have a requirement that a function have an identity
element.  Thus, we can treat "not" operation on the smallest possible
logical domain as a function (true = not false, false = not true).
This constraint only applies when there is an identity.

As for the grammatical issue:  if we have an identity operation i for
an arrow, this means that the fmap of that arrow has to have an
identity operation and it has to have an identity element which is the
mapped version of the original domain's identity element (for every
identity element in the original domain).

For example: a mapping from arbitrary function F4 to my previously
defined F1 would be a valid fmap.

Q6 seems like a slippery issue to me.  On the one hand, I have not
seen anything explicitly stating that the member being constructed
matters (other than the identity element constraint, from Q5).  On the
other hand, the use of fmap to represent a transformation on leaves of
a tree structure suggests that there must be very strong constraints
on the member being constructed.  So obviously I am missing something
here.

But let's take a step back and consider some likely candidates for J functors.

First off "n is probably a functor for valid integer values of n.
Also &.> and L:n are probably functors.  In fact &.g is probably a
functor, if g is limited sufficiently.  :: g is probably also a
functor in the sense that f :: g an identity transform over the
original domain of f (f :: g derives a new function which is identical
to f over f's original domain and is identical to g over some other
domain).

I suppose the issue which concerns me is that &.g isn't a general
structure access mechanism in J.  We could instead use 2 :'(u@)(@v)'
and we still don't have anything like a J structural definition or
structure access mechanism.

One issue here seems to me to be that while Haskell uses currying
routinely to express routine issues like argument passing and list
traversing, currying has a more limited role in J.  In J, we derive
new verbs when we are using adjectives and conjunctions but unless we
are dealing with gerunds we probably do not derive new verbs when
evaluating verbs -- even [especially?] when we are dealing with lists
of data.  (Then again, we can think of a lookup table as a function,
so perhaps I should state this differently.)

Another issue is that every J verb has a rank.  So, in some sense you
can't have a J function without having a canned functor associated
with it.

Another issue is that in J we have nouns (and verbs, adverbs and
conjunctions) for our type system.  Nouns are quite expressive but
ultimately they are a single data type.  From a J point of view, when
we are reasoning about types, we are typically dealing with subsets of
the values which can be represented using nouns.

So, perhaps, for J users [like me] to really understand the
significance of Haskell functors for Haskell programmers, we [I?] need
to have a better understanding of how Haskell structural functions are
defined in Haskell?

-- 
Raul
----------------------------------------------------------------------
For information about J forums see http://www.jsoftware.com/forums.htm

Reply via email to