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 correspondence between this typeclass and its mathematical (i.e., Category Theoretic) analogue. Generalizing:
Question 1: Does every every typeclass in Haskell whose members are of kind * -> *
correspond to some function between categories?
Now consider a typeclass whose members are of type *
. For example, one could imagine a type class Group
which corresponds to the category of Groups (technically, Group
would be a subcategory of Hask
whose objects comprise all of Haskell's types). Generalizing:
Question 2: Does every typeclass in Haskell whose members are of kind *
correspond to some category (technically: some subcategory of Hask
)?
From this, the next general question can be asked:
Question 3: Do typeclasses of kind equal to or higher than * -> * -> *
correspond to some category theoretic notion?
And really, this entire question could be summarized as follows:
General Question: Does every Haskell type class correspond to some category theoretic notion?
EDIT: At the very least, it seems you could say that since every type class contains some set of Haskell types as its members, you could view every type class as some subcategory of Hask
(closed under .
and making use of id
).
What's a typeclass in Haskell? A typeclass defines a set of methods that is shared across multiple types. For a type to belong to a typeclass, it needs to implement the methods of that typeclass. These implementations are ad-hoc: methods can have different implementations for different types.
Category theory can be helpful in understanding Haskell's type system. There exists a "Haskell category", of which the objects are Haskell types, and the morphisms from types a to b are Haskell functions of type a -> b .
Everything in Haskell has a type, so the compiler can reason quite a lot about your program before compiling it. Unlike Java or Pascal, Haskell has type inference. If we write a number, we don't have to tell Haskell it's a number.
The classes used by Haskell are similar to those used in other object-oriented languages such as C++ and Java. However, there are some significant differences: Haskell separates the definition of a type from the definition of the methods associated with that type.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With