So, assuming f
and g
are proper functors, forall a. f a -> g a
is a natural transformation. We could make it a bit prettier:
type f ~> g = forall a. f a -> g a
Natural transformations like this let us form a category of Haskell Functors, so what you have is a functor from that to some other category.
Following the steps of normal Haskell Functors, it would perhaps make sense to have x
be an endofunctor, mapping Functors to other Functors. This is similar, but not identical, to what you have:
class FFunctor x where
ffmap :: (f ~> g) -> (x f ~> x g)
However, in your case x f
and x g
are not functors, and x f -> x g
is a normal function rather than a natural transformation. Still, the pattern is close enough to be intriguing.
With this in mind, it seems that x
is still an example of a functor, just between two different categories. It goes from the category of Functors to the category of x
s with different structures. Each possible x
, like Foo
, forms a category with objects like Foo []
and Foo Maybe
and transformations between them (Foo [] -> Foo Maybe
). Your interleaveHomomorphism
function "lifts" natural transformations into these x-morphisms
, just like fmap
"lifts" normal (a -> b
) functions into functions in the image of the functor (f a -> f b
).
So yeah: your typeclass is a functor just like Functor
, except between two different categories. I don't know of a specific name for it, largely because I don't know a specific name for constructs like x
.
More generally, I'm not even sure a specific name would make sense. At this point, we'd probably like a nice generic functor typeclass that go between any two categories. Maybe something like:
class (Category catA, Category catB) => GFunctor f catA catB where
gfmap :: catA a b -> catB (f a) (f b)
This probably already exists in a library somewhere.
Unfortunately, this particular approach to defining different functors would require a bunch of extra newtype noise since (->)
is already a category. In fact, getting all the types to line up properly is going to be a bit of a pain.
So it's probably easiest just to call it an XFunctor
or something. Besides, just imagine the pun potential!
EDIT: It looks like categories
provides a CFunctor
type like this, but a bit cleverer:
class (Category r, Category s) => CFunctor f r s | f r -> s, f s -> r where
cmap :: r a b -> s (f a) (f b)
However, I'm not certain that even this is general enough! I think that we might want it to be more polymorphic over kinds too.
GFunctor
exists; indeed mathematicians would consider endofunctors like the ones we have in Hask very much a special case and usually deal with functors between different categories. Edward parameterisesclass (Category r, Category t) => Functor f r t | f r -> t, f t -> r
where fmap :: r a b -> t (f a) (f b)
. – Trauma