Your suggestions ignore some pretty important information about the existing definition of Functor , because you could not handle the details of writing what would happen to the class member function.
class MultiFunctor ae where mfmap :: (e -> ??) -> a -> ???? instance MultiFunctor (Maybe a) a where mfmap = ???????
An important property of fmap at the moment is that its first argument can change types. fmap show :: (Functor f, Show a) => fa -> f String . You can't just throw it away, or lose most of the fmap value. So really, the MultiFunctor will need to be more like ...
class MultiFunctor stab | s -> a, t -> b, sb -> t, ta -> s where mfmap :: (a -> b) -> s -> t instance (a ~ c, b ~ d) => MultiFunctor (Maybe a) (Maybe b) cd where mfmap _ Nothing = Nothing mfmap f (Just a) = Just (fa)
Notice how incredibly difficult it is to try to make a conclusion that is at least as close as possible. All functional dependencies are available for instance selection without annotating types throughout the place. (Maybe I missed a couple of possible functional dependencies!) The instance itself created some crazy type equality constraints to provide a more reliable instance selection. And the worst part is even worse properties for reasoning than fmap .
Suppose my previous instance does not exist, I could write such an instance:
instance MultiFunctor (Maybe Int) (Maybe Int) Int Int where mfmap _ Nothing = Nothing mfmap f (Just a) = Just (if fa == a then a else fa * 2)
It’s broken, of course, but it’s broken in a new way, which was not before. An important part of the Functor definition is that the types a and b in fmap do not appear anywhere in the instance definition. Just looking at the class is enough to tell the programmer that the behavior of fmap cannot depend on the types a and b . You get this warranty for free. You do not need to believe that the instances were written correctly.
Since fmap provides you this guarantee for free, you don’t even need to check the Functor laws when specifying an instance. It is enough to check the law fmap id x == x . The second law is distributed free of charge when the first law is proved. But with that broken mfmap that I just provided, mfmap id x == x true, although the second law is not.
As an mfmap developer, you have more work to prove that your implementation is correct. As a user of this, you should trust the implementation more because the type system cannot guarantee as much.
If you have developed more complete examples for other systems, you will find that they have the same problems if you want to support the full fmap functionality. And that is why they are not actually used. They add more complexity just for a small increase in utility.