In category theory a category is composed of objects and morphisms between the objects. A functor is a mapping from one category to another category. There are two parts to every functor a mapping for the objects in a category and a mapping for the morphisms.
Category theory works in Haskell because Haskell is composed of pure functions. The Hask category is composed of types (the objects) and functions (the morphisms between the types). The Functor type class in Haskell isn't exactly a true category theory functor. The Functor type class only maps functions to functions (leaving out the types). Additionally, it really only applies to one kind of functor ... like a functor from Hask to some subset of Hask. So compared to Category theory functor it's kind of lame, but compared to the alternative of not having it it's kind of awesome.
class KFunctor f where
kfmap :: (a -> b) -> f a -> f b
data KEither a b = KLeft a | KRight b
data KMaybe a = KJust a | KNothing
instance KFunctor KMaybe where
kfmap _ KNothing = KNothing
kfmap f (KJust a) = KJust (f a)
instance KFunctor (KEither e) where
kfmap _ (KLeft e) = KLeft e
kfmap f (KRight r) = KRight (f r)
jabber x = x + 5
kfmap jabber $ KNothing -> KNothing
kfmap jabber $ KJust 5 -> KJust 10
kfmap jabber $ KLeft "message" -> KLeft "message"
kfmap jabber $ KRight 5 -> KRight 10
The KMaybe KFunctor is relatively straightforward (at least as far as these things go). The (KEither e) KFunctor on the other hand isn't so obvious. Writing out the code for (KEither e) KFunctor helped me a lot in figuring out what was actually going on. So again remember that KFunctor can also be defined like so:
class KFunctor (f :: * -> *) where
And that KEither is going to have a kind of * -> * -> *, but if we curry it, then we will get the desired kind for the KFunctor ... ie (KEither e) :: * -> *. Now it personally bothered me that I had no idea about what the "e" was in the (KEither e). But really the "e" doesn't actually matter because it doesn't show up in the type signature of kfmap ( ie (a -> b ) -> f a - > f b ). The consequence is that the function referenced the kfmap ( ie (a -> b) ) will never have any way of dealing with the "e". Which is totally fine depending on what usage you want to get out of it. In functor's case we are more or less using it in the same way as the KMaybe is being used ... the only difference is that instead of KNothing you can actually have some sort of error message. For a more complicated usage of KEither, KFunctor won't do the trick and you'll have to use something else.