There is a Data.Distributive , which is a double of Data.Traversable . It provides a distribute function that can be specialized, for example. like f (Stream a) -> Stream (fa) or distribute :: f (Vec na) -> Vec n (fa) . The final example is a homogeneous version of your family of functions.
But we can generalize Data.Distributive little in the same way that lenses generalize functors. Enter Colens :
type Colens stab = forall f. Functor f => (fa -> b) -> fs -> t
Here is the mirror of Control.Lens.Each :
class Coeach stab | s -> a, t -> b, sb -> t, ta -> s where coeach :: Colens stab instance (a~a', b~b') => Coeach (a,a') (b,b') ab where coeach fp = (f $ fst <$> p, f $ snd <$> p) instance (a~a2, a~a3, b~b2, b~b3) => Coeach (a,a2,a3) (b,b2,b3) ab where coeach fp = ... ...
And just like with each , we can iterate over tuples
each_id1 :: Applicative f => (fa, fa) -> f (a, a) each_id1 = each id each_id2 :: Applicative f => (fa, fa, fa) -> f (a, a, a) each_id2 = each id
with coeach , we can write through tuples:
coeach_id1 :: Functor f => f (a, a) -> (fa, fa) coeach_id1 = coeach id coeach_id2 :: Functor f => f (a, a, a) -> (fa, fa, fa) coeach_id2 = coeach id
However, it is still uniform. I don’t know
lens much, so I can’t say whether heterogeneous
each and corresponding
coeach .