Hi @Matthew, here's my take on the puzzle:
I've created two classes, one for each preservation property
class PreservesCoproduct f where
extractCoproduct :: f (Either a b) -> Either (f a) (f b)
introduceCoproduct :: Either (f a) (f b) -> f (Either a b)
class PreservesProduct f where
extractProduct :: f (a, b) -> (f a, f b)
introduceProduct :: (f a, f b) -> f (a, b)
And then two implementations: (I've needed to add the extentions FlexibleInstances and UndecidableInstances)
instance Adjunction f g => PreservesCoproduct f where
extractCoproduct = rightAdjunct $ fmap Left . unit ||| fmap Right . unit
introduceCoproduct = fmap Left ||| fmap Right
instance Adjunction f g => PreservesProduct g where
extractProduct = fmap fst &&& fmap snd
introduceProduct = leftAdjunct $ counit . fmap fst &&& counit . fmap snd
Both `introduceCoproduct` and `extractProduct` only need `f`(or `g`) to be functors, so they can be put into another instance with a less strong condition.
Function `introduceProduct` is the uncurried version of `f a -> f b -> f (a, b)` which can be found in the
[monoidal presentation](https://en.wikibooks.org/wiki/Haskell/Applicative_functors#The_monoidal_presentation) for **applicative functors**.
The last one, `extractCoproduct`, I cannot relate with anything I know (besides being the dual of the "monoidal" one).
And that's all (for now ;-D)