This is even worse in Rust, which requires you to have a separate implementation for every effect as well (since it doesn't have higher kinded types)
This is more a weakness of Haskell's standard library (which is despite its reputation not very friendly to category theory) than an inherent problem with monads. A more general `map` would look something like this
class Category c => FunctorOf c f where
map :: c a b -> c (f a) (f b)
fmap :: FunctorOf (->) f => (a -> b) -> f a -> f b
fmap = map
type Kleisli m a b = a -> m b
mapM :: (Monad m, FunctorOf (Kleisli m) f) => (a -> m b) -> f a -> m (f b)
mapM = map
type Op c a b = c b a
contramap :: FunctorOf (Op (->)) f => (a -> b) -> f b -> f a
contramap = map
type Product c d (a,x) (b,y) = (c a b, d x y)
bimap :: FunctorOf (Product (->) (->)) f => (a -> b, x -> y) -> f (a, x) -> f (b, y)
bimap = map
-- and so on
but this would require somewhat better type-level programming support for the ergonomics to work out.It's really trickier than algebraic effects make it seem though. Haskell-ish "monad transfomers" as a stack of wrappers may pick concrete ordering of effects in advance (e.g. there's difference between `State<S, Result<E, T>>` and `Result<E, State<S, T>>`, using Rust syntax), but effect systems like one in Koka either have to do the same decision by using specific order of interpreters, or by sticking to single possible ordering, e.g. using one, more powerful monad. And then there're questions around higher order effects - that is, effects with operations that take effectful arguments - because they have to be able to "weave" other effects through themselves while preserving their behaviour, and this weaving seems to be dependent on concrete choice of effects, thus not being easily composable. In a sense, languages like Koka or Unison have to be restricted in some way, giving up on some types of effects. I'm not saying that's a bad thing though, it's still a improvement over having single effect (IO) or no effects at all.