I'm a bit confused, and need someone to set me straight. Lets outline my current understanding:
Where E is an endofunctor, and A is some category:
E : A -> A.
Since all types and morphisms in Haskell are in the Hask category, is not any functor in Haskell also an endofunctor? F : Hask -> Hask.
I have a good feeling that I'm wrong, and oversimplifying this somehow, and I'd like someone to tell me what an idiot I am. Thanks.
You may want to clarify whether you're asking about "functors in Haskell", or Functors. It's not always clear what category is being assumed when Category Theory terms are used in Haskell.
But yes, the default assumption is Hask, which is taken to be the category of Haskell types with functions as morphisms. In that case, an endofunctor F on Hask would map any type A to a type F(A) and any function f between two types A and B to a function F(f) between some types F(A) and F(B).
If we then limit ourselves to only those endofunctors which map any type a to a type (f a) where f is a type constructor with kind * -> *, then we can describe the associated map for functions as a higher-order function with type (a -> b) -> (f a -> f b), which is of course the type class called Functor.
However, one can easily imagine well-behaved endofunctors on Hask which can't be written (directly) as an instance of Functor, such as a functor mapping a type a to Either a t. And while there's obviously not much sense in a functor from Hask to some other category entirely, it's reasonable to consider a (contravariant) functor from Hask to Haskop.
Beyond that, instances of Functor necessarily map from the entire category Hask onto some subset of it that, thus, also forms a category. But it's also reasonable to talk about functors between subsets of Hask. For instance, consider a functor that sends types Maybe a to [a].
You may wish to peruse the category-extras package, which provides some Category Theory-inspired structures embedded within Hask instead of assuming the entirety of it.
Even if ultimately, you manipulate Hask, there are a lot of other categories that can be built on Hask, which can be meaningful for the problem at hand:
Hask^op, which is Hask with all arrows reversed
Hask * Hask, functors on it are bifunctors
Comma categories, ie. objects are morphisms to a fixed object a, morphisms are commutative triangles
Functor categories, morphisms are natural transformations
Algebra categories
Monoidal categories
Kleisli categories
...
grab a copy of Mac Lane's Categories for the Working Mathematician to have definitions, and try to find by yourself the problem they solve in Haskell. Especially choke on adjoint functors (which are initial/terminal objects in the right category) and their relationship with monads.
You'll see that even if there is one big category (Hask, or perhaps "lifted objects from Hask with the right arrows/products/...", which encapsulates the language choices of Haskell such as non-strictness and lazyness), proper derived categories are expressive.
A possibly relevant (or at least interesting) discussion specifically regarding monads is found in the paper "Monads need not be endofunctors":
http://www.cs.nott.ac.uk/~txa/publ/Relative_Monads.pdf
Related
This answer from a Category Theory perspective includes the following statement:
...the truth is that there's no real distinction between co and contravariant functor, because every functor is just a covariant functor.
...
More in details a contravariant functor F from a category C to a category D is nothing more than a (covariant) functor of type F : Cop→D, from the opposite category of C to the category D.
On the other hand, Haskell's Functor and Contravariant merely require fmap and contramap, respectively, to be defined for an instance. This suggests that, from the perspective of Haskell, there exists objects that are Contravariant but are not Functors (and vice versa).
So it seems that in Category Theory "there's no real distinction between co and contravariant functors" while in Haskell there is a distinction between Contravariant and Functor.
I suspect that this difference has something to with all implementation in Haskell happening in Hask, but I'm not sure.
I think I understand each of the Category Theory and Haskell perspectives on their own, but I'm struggling to find an intuition that connects the two.
It's for convenience.
One could get by with a more general Functor class, and define instances for endofunctors on Hask (corresponding to our existing Functor) and functors from Hask^op to Hask (corresponding to our existing Contravariant). But this comes at a figurative cognitive cost and a quite literal syntactical cost: one must then rely on type inference or type annotations to select an instance, and there are explicit conversions (named Op and getOp in the standard library) into and out of Hask^op.
Using the names fmap and contramap relaxes both costs: readers do not need to run Hindley-Milner in their head to decide which instance is being selected when it is unambiguous, and writers do not need to give explicit conversions or type annotations to select an instance in cases where it is ambiguous.
(I am actually rewriting history a little bit here. The real reason is because the language designers thought the specialized Functor would be useful and hadn't imagined or didn't see a need for a more general Functor. People came along later and noticed it would be useful, sometimes. But experience with the generalized Functor class shows that can be tedious, and that specialized classes for the most common cases turns out to be a surprisingly good fit after all, for the reasons described above.)
Imagine for a minute we had something like the following.
class MoreAccurateFunctor c d f where
fmap :: c a b -> d (f a) (f b)
Since (->) is an instance of Category (this is Hask), we would have that Functor ~ MoreAccurateFunctor (->) (->).
Now, imagine we have Dual (->), the dual category of (->) (this would be HaskOp and we would have Dual (->) a b ~ (b -> a)), we would have that Contravariant ~ MoreAccurateFunctor (Dual (->)) (->).
I don't know if this helps but the idea is to point out the fact that Functor and Contravariant are two specialisations of MoreAccurateFunctor while this latter class is closer to the definition of functor in category theory.
Mathematically, considering contravariant functors as a distinct class of functors is just a notational convenience; the contravariant functor F : C -> D can always be defined as a covariant functor F' : C^{op} -> D, so getting rid of the idea of contravariant functors would just force you to talk about the opposite category explicitly.
In Haskell, the Functor class represents an endofunctor on the (assumed) category Hask. There is no convenient way to represent HASKOP directly (or at least, not in a form that helps us define functors from that category), nor is there a typeclass that defines exofunctor*, so instead we define the Contrafunctor class whose contramap function can reverse the arrow from Hask "on demand", so to speak.
* Is "exofunctor" a real term? I just made it up to indicate a functor that is not an endofunctor.
Hask is usually thought to be the category whose objects are types and morphisms are functions.
However, I've seen Conor McBride (#pigworker) warn against the use of Hask multiple times (1, 2, 3):
I would discourage talk of "the Hask Category" because it subconsciously conditions you against looking for other categorical structure in Haskell programming.
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.
I wish people wouldn't call it "Hask", though: it threatens to limit the imagination.
What other categories can we see in Haskell?
In one of his answers, he touches upon some of these ideas, but I wonder if someone could expand upon it; and I wonder if there are even more examples.
[...] 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.
Constraints in Haskell also form a category. The objects are the constraints, and the arrows mean "this constraint implies this other constraint". So every constraint implies itself, and there's an arrow between Monad f and Applicative f, between Ord a and Eq a and between Ord a and Ord [a].
It is a thin category, so there is at most one arrow between two objects.
Gabriel Gonzalez has blogged about this. Here's one such post:
http://www.haskellforall.com/2012/08/the-category-design-pattern.html
In it, he calls Hask "the function category", and also discusses "the Kleisli category" and "the pipes category." These are all examples of instances of the Category typeclass in Haskell. The Category typeclass in Haskell is a subset of the categories you can find in Haskell.
I once uploaded an educational package that demonstrates one example of this. I called it MHask.
http://hackage.haskell.org/package/MHask
Copied from the hackage page:
MHask is the category where
The objects are Haskell types of kind (* → *) that have an instance of Prelude.Monad
An arrow from object m to object n is a Haskell function of the form (forall x. m x → n x)
Arrow composition is merely a specialization of Haskell function composition
The identity arrow for the object m is the Prelude.id function in Haskell, specialized to (forall x. m x → m x)
Caveat emptor; I have not looked at this in a long time. There may be mistakes.
As I know we can derive functor for free via Coyoneda.
But exists some haskell package http://hackage.haskell.org/package/free-functors
And my question, what is difference between Coyoneda and http://hackage.haskell.org/package/free-functors-0.8.1/docs/src/Data-Functor-Free.html#Free
The key here is to understand what it means for a type constructor not to be a functor. It means that it's defined on objects and not on morphisms. But we can describe it as a functor too, if we choose a different source category. For every category C, you can define a discrete category |C|, which has the same objects as C, but no morphisms other than the identity morphisms. A "non-functor" is just a functor from |C| to C. There is a trivial injection functor J from |C| to C that is identity on objects and morphisms (of which there are only identity morphisms). So let's see:
Coyoneda is defined as a left Kan extension of a functor f along the identity functor. It requires f to be a functor.
Free functor looks like Coyoneda, but it's really the left Kan extension of a functor f from |C| to C along J. Strictly speaking J is not identity, but it's close enough, hence the abuse of notation.
The free functor from Sjoerd Visscher's library extends this idea even further. Roughly speaking, a type class in Haskell defines a subcategory of Hask. His free functor is then the left Kan extension of a functor f from that subcategory (or the discrete version of it) to Hask, along the injection of that subcategory into Hask.
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).
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.
Conclusions
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.
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.
Can someone explain in simple terms the difference between the two? I'm not fully understanding the part where monads are endofunctors versus being just functors.
A functor may go from one category to a different one, an endofunctor is a functor for which start and target category are the same.
Same as with endomorphisms versus morphisms.
Now, why must monads be endofunctors?
There is the famous quote that "Monads are just monoids in the category of endofunctors". Fortunately, somebody else has already explained that rather well in this answer.
The key point why a monad has to be an endofunctor, is that join, as it is called in Haskell, or µ, as it is usually called in category theory, is part of the definition¹ of a monad. Now
Prelude Control.Monad> :t join
join :: Monad m => m (m a) -> m a
so the result of applying the functor m to an object (in Hask, the category of Haskell types as objects and functions as morphisms, a type) must be an object that m can again be applied to. That means it must belong to the category that is the domain of the functor m.
A functor can only be composed with itself if its domain and codomain are the same [strictly, if its codomain is a subcategory of its domain], in other words, if it is an endofunctor. Since composability with itself is part of the definition of a monad, monads are a fortiori endofunctors.
¹ One definition, one can alternatively define a monad using (>>=) or bind and have join as a derived property.