In that code, I made a more powerful version of Functor that can handle more interesting categories.
In that setting, the fact that we use 'curried' types matters quite a bit. The usual way to think of a bifunctor is as a functor from a product category, which would like like something of kind (*,*) -> *. And you can define such a beast in Haskell. The "truly unbiased" tuple some folks have been hollering for in this thread would ideally have this sort of kind. After all, then it can't be Foldable, Traversable, etc.
But in this more general setting where we can have Functors that go to other categories than just Hask, haskell's "Bifunctor", which has kind * -> * -> * is a functor that to a functor category. We can curry/uncurry the kinds involved to go back and forth between these two representations and that always works in a category that is locally small like what we can define in Haskell. 'Bi' is a form of type level uncurry.
In the hask code, Either is a functor from Hask to [Hask,Hask], where the latter is the category of functors from Hask -> Hask.
This of course, fundamentally relies on the fact that Either a being a Functor. Mutatis mutandis for (,) a being a functor.
Once you have those things you can use runNat from the package in question to move 'backwards' n arguments an:d map over any field you want, be it a bifunctor, trifunctor, whatever. Contravariant becomes a functor from an opposite category, and large numbers of classes we have just collapse away into one thing: Functor.
Bifunctor ceases to be a primitive notion and just becomes a derived fact from the existence of those two Functor instances.
I find this to be far more enlightening than a arbitrarily adopting an unbiased (,) and making the entire world adopt mutually incompatible data types. YMMV.
-Edward