Consider a type class whose members are of type * -> *
. For example: the Functor
typeclass. It is a well-known fact that, in Haskell, there is a cor
When interpreted sufficiently pedantically, the answer to all of these questions is "yes", but for uninformatively trivial reasons.
Every category C restricts to a discrete subcategory |C| with the same objects as C but only identity morphisms (and hence no interesting structure). At the very least, operations on Haskell types can be boringly interpreted as operations on the discrete category |*|
. The recent "roles" story amounts to (but is not spun as) an attempt to acknowledge that the morphisms matter, not just the objects. The "nominal" role for types amounts to working in |*|
rather than *
.
(Note, I dislike the use of "Hask" as the name of the "category of Haskell types and functions": I fear that labelling one category as the Haskell category has the unfortunate side-effect of blinding us to the wealth of other categorical structure in Haskell programming. It's a trap.)
Being differently pedantic, I'd note that you can make up any old crap as a typeclass over any old kind, with no interesting structure whatsoever (but with trivial structure that can still be talked about categorically, if one must). However, the classes you find in the library are very often structure-rich. Classes over * -> *
are often, by design, subclasses of Functor
, requiring the existence of certain natural transformations in addition to fmap
.
For question 2. Yes, of course a class over *
gives a subcategory of *
. It's no problem to chuck objects out of a category, because the categorical requirement that identities and composites exist require morphisms to exist, given objects, but make no demands about which objects exist. The fact that it's boringly possible makes it a boring fact. However, many Haskell typeclasses over *
give rise to much more interesting categories than those arising just as subcategories of *
. E.g., the Monoid
class gives us a category where the objects are instances of Monoid
and the arrows are monoid homomorphisms: not just any old function f
from one Monoid
to another, but one which preserves the structure: f mempty = mempty
and f (mappend x y) = mappend (f x) (f y)
.
For question 3, well, in that there's a ton of categorical structure lurking everywhere, there's certainly a ton of categorical structure available (possibly but not necessarily) at higher kinds. I'm particularly fond of functors between indexed families of sets.
type (s :: k -> *) :-> (t :: k -> *) = forall x. s x -> t x
class FunctorIx (f :: (i -> *) -> (j -> *)) where
mapIx :: (s :-> t) -> (f s :-> f t)
When i
and j
coincide, it becomes sensible to ask when such an f
is a monad. The usual categorical definition suffices, even though we've left * -> *
behind.
The message is this: nothing about being a typeclass inherently induces interesting categorical structure; there is plenty of interesting categorical structure which can usefully be presented via type classes over all manner of kinds. There are most certainly interesting functors from *
(sets and functions) to * -> *
(functors and natural transformations). Don't be blinded by careless talk about "Hask" to the richness of categorical structure in Haskell.
You can imagine mappings between categories that don't preserve their categorical structure. But they are not interesting. In category theory we want to work with structure-preserving mappings, and these are called functors.
A bare type constructor of the kind *->*
has no provision for mapping morphisms. So the best you can do, as @pigworker explained, is to interpret them as functors from |C| to C, only because |C| has no nontrivial morphisms to be mapped.
Haskell Functor
is an endofunctor, as long as it satisfies functor laws (which cannot be enforced in Haskell). An endofunctor is not an object in the category that it maps, therefore is not a type. But that's also true with morphisms -- they are not objects. There is, however, a way to represent morphisms as objects, if the category supports exponentials. Haskell's category of types is Cartesian closed, so it supports exponentials.
So does it also provide objects (types) representing endofunctors? As far as I know, it doesn't. So a functor is not a member of Hask (or whatever we call it).
Incidentally, a mapping of the kind *->*->*
can have a nontrivial categorical interpretation as a bifunctor -- a structure-preserving functor from the product category CxC to C. See the definition of Bifunctor
in Haskell.
One of the problems here is that category theory, a.k.a. general abstract nonsense, is a theory that you can use to talk about almost anything in mathematics. So everything that we are talking about can be expressed using the language of category theory, but we might not produce any interesting results.
Does every every typeclass in Haskell whose members are of kind
* -> *
correspond to some function between categories?
No. This question contains a type error! A function maps sets to sets, but a category is not a set. (Put another way, functions are morphisms in the category Set.) Categories are formulated using classes, often proper classes, so you cannot feed a category to a function.
We would call objects in * -> *
morphisms in the category of Haskell types. A subcategory of this category is the category of functors between Haskell types, which is called Functor
.
Does every typeclass in Haskell whose members are of kind
*
correspond to some category (technically: some subcategory of Hask)?
Yes. This is true, but it's not terribly interesting. Just remove every object from Hask that is not in your typeclass, and remove any morphism in Hask that does not consume and produce elements from your typeclass, and you are left with a subcategory of Hask. This category should have at least one object, ⊥
, and at least one morphism, id
.
Do typeclasses of kind equal to or higher than
* -> * -> *
correspond to some category theoretic notion?
Yes. Again, this won't be very interesting. Let's take a typeclass X
with kind * -> * -> *
.
Is X
an object in a category of typeclasses with the same kind? Well, yes. But this category isn't very interesting, because it's hard to imagine any nontrivial morphisms.
Is X
a morphism in some category? No, because it cannot be composed.
Is X
a functor mapping a subcategory of types in Hask to a subcategory of morphisms on types in Hask? Sure, but we would have to have some special knowledge that both X Y a b
and X Z a b
are permissible for the same a b
before we allow morphisms into our starting subcategory on types of Hask.
This doesn't seem to me like it will produce any useful insights, which is not really surprising because we don't really know anything about X
.
Category theory is one of those tools that is really quite easy to overthink and overapply. If you are not interested in category theory as a subject of study in and of itself, my recommendation is to find concrete motivations to use it. Specific typeclasses (functors, lenses, monads, comonads, etc) will sometimes provide you with enough structure or "raw mathematical material" from which you can construct an interesting proof in category theory. But the study of typeclasses in general may be a bit more abstract than it is useful.