Given the following definitions of Functor and Coproduct from fp-course:

class Functor f where
  (<$>) ::
    (a -> b)
    -> f a
    -> f b

class Functor f => Applicative f where
  pure ::
    a -> f a
  (<*>) ::
    f (a -> b)
    -> f a
    -> f b

class Functor t => Traversable t where
  traverse ::
    Applicative f =>
    (a -> f b)
    -> t a
    -> f (t b)

data Coproduct f g a =
  InL (f a)
  | InR (g a)

Is there a cleaner or more succinct way to implement the following?

instance (Traversable f, Traversable g) =>
  Traversable (Coproduct f g) where
  traverse ::
    Applicative h =>
    (a -> h b)
    -> Coproduct f g a
    -> h (Coproduct f g b)
  traverse f (InL fa) = InL <$> traverse f fa
  traverse f (InR ga) = InR <$> traverse f ga  

Here is how Edward Kmett implemented traverse in the Data.Functor.Coproduct package.

instance (Traversable f, Traversable g) => Traversable (Coproduct f g) where
  traverse f = coproduct
    (fmap (Coproduct . Left) . traverse f)
    (fmap (Coproduct . Right) . traverse f)

While he writes in a points free style and uses Either under the hood instead of your bespoke Coproduct instance, the result is still the same. Apply the supplied function to either the left or right branch and wrap the result back in the constructor.

I do not see a better way to than the implementation given unless η-conversion to points-free is appealing for some reason.


type Coproduct f g a = Either (f a) (g a) has a Traversal via the lens library:

choosing traverse traverse :: (Traversable tl, Traversable tr, Applicative f)
  => (a -> f b) -> Coproduct tl tr a -> f (Coproduct tl tr b)

I'd expect your usecase to have an easier time using lens directly.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.