Type classes are rather fragile: it's really hard to make changes to a type class once because it can often break code downstream.
Type classes are in a sense like implicit parameters, with the additional constraint of coherency. If we think of them as such, then defining an instance such as
instance Monad Maybe where
(>>=) = …
(>>) = …
return = …
is akin to having a particular anonymous variable blessed by the compiler
instance of ( Monad
{ (>>=) = …
, (>>) = …
, return = …
} :: Monad Maybe )
What if we allowed functions that maps to instances?
defInst_Monad_simple :: Applicative m => (forall a b . m a -> (a -> m b) -> m b) -> Monad m
defInst_Monad_simple bind =
Monad
{ (>>=) = bind
, (>>) = \ m k -> m `bind` const k
, return = pure
}
instance of (defInst_Monad bindFunc_Maybe :: Monad Maybe)
What if we allowed multiple instances to be defined simultaneously?
defInst_Functor_Applicative_Monad :: … -> (Functor m, Applicative m, Monad m)
defInst_Functor_Applicative_Monad = …
instance of (defInst_Functor_Applicative_Monad … :: (Functor Maybe, Applicative Maybe, Monad Maybe))