In Haskell, we've got the functions:
fmap :: Functor f => (a -> b) -> (f a -> f b)
pure :: Applicative f => a -> f a
(<*>) :: Applicative f => f (a -> b) -> (f a -> f b)
(=<<) :: Monad f => (a -> f b) -> (f a -> f b)
I can write fmap in terms of pure and (<*>) like so:
fmap x = (<*>) (pure x)
I can also write (<*>) in terms of (=<<) and pure like so:
m1 <*> m2 = (\x -> (\y -> pure (x y)) =<< m2) =<< m1
Noticed earlier I flipped the bind operator from it's usual (>>=) to (=<<). The reason I did this is because I now want to generalise the functions like so:
fmap :: (Functor f, Category cat) => cat a b -> cat (f a) (f b)
pure :: (Applicative f, Category cat) => a -> f a
(<*>) :: (Applicative f, Category cat) => f (cat a b) -> cat (f a) (f b)
(=<<) :: (Monad f, Category cat) => cat a (f b) -> cat (f a) (f b)
Interestingly, I can still write fmap in terms of pure and (<*>) exactly the same as before:
fmap x = (<*>) (pure x)
However, I work out how can't write (<*>) in terms of pure and (=<<). Is it possible? I suspect it isn't, but I also suspect it's possible to write (<*>) in terms of pure and (=<<) for more categories other than ordinary functions. I also suspect to do this my category needs a few extra functions.
So I'm guessing perhaps I need a class and a few instances, like so:
class Category cat => SpecialCategory cat where
...
instance SpecialCategory (->) where
...
instance SpecialCategory SomeOtherCategory where
...
With this approach I should be able to define any special characteristics a category needs to be able to define (<*>) in terms of (=<<) and pure.
What are those characteristics? Or am I completely off track in my approach to making this generalisation?