I'm do not believe that that the FlexibleContexts-based instance is the best available option. The available options are:
instance Semigroup (f (g a)) => Semigroup (Compose f g a) -- FlexibleContexts
OR
instance (forall x. Semigroup x => Semigroup (f x), forall x. Semigroup x => Semigroup (g x), Semigroup a) => Semigroup (Compose f g a) -- QuantifiedConstraints
There is a third option available which is to add Semigroup1 and Monoid1 typeclasses, but since the QuantifiedConstraints extension obsoletes such boilerplate, this option is not be considered here. David Feuer has pointed out in another thread that this context is satisfied by strictly more types than the context on the QuantifiedContstraints-based instance. However, I argue that the cost of this flexibility is composition. That is, the context does not break down into smaller contexts that can be satisfied individually. By constrast, the QuantifiedConstraints-based instance offers a context comprised of three distinct constraints. This is the same strategy employed by the Eq, Ord, Show, and Read instances. That is to say that the QuantifiedConstraints-based instances is consistent in spirit with the existing instances. And as the the Eq1/Ord1/...-based instances for Compose enable reasoning compositionally about constraints, so does QuantifiedConstraints-based Semigroup instance. Consider this contrived example that illustrates the principle:
foo :: ??? => Compose IO Maybe a -> Compose IO Maybe a -> Compose IO Maybe a
foo = (<>)
What context should fill in the question marks? With the QuantifiedConstraints-based instance, we would write `Semigroup a` (since the other two constraints are satisfied), but with the FlexibleContexts-based instance, we would write `Semigroup (IO (Maybe a))`. Bummer. I've encountered real scenarios where this is actually a problem although it would be tedious to explain them here. I'm happy to hear differing opinions or agreement. One way or another, we really ought to add something.