7

Is there a name for this family of operations?

Functor f => f (a, b) -> (f a, f b)
Functor f => f (a, b, c) -> (f a, f b, f c)
...
Functor f => f (a, b, ..., z)  -> (f a, f b, ..., f z)

They're easy to implement, just trying to figure out what to call it.

\fab -> (fst <$> fab, snd <$> fab)

For me, it came up in the context of f ~ (x ->).

rampion
  • 87,131
  • 49
  • 199
  • 315
  • 2
    I guess these are just in the opposite direction as `uncurry (liftA2 (,)) :: Applicative f :: (f a, f b) -> f (a,b)`, so this might have something to do with [comonoidal functors](https://cstheory.stackexchange.com/a/16264/20074) – rampion Sep 11 '17 at 16:24
  • also relevant https://stackoverflow.com/questions/9190352/abusing-the-algebra-of-algebraic-data-types-why-does-this-work – jberryman Sep 11 '17 at 17:00

2 Answers2

8

In your specific context f ~ (x ->), I think they can be called "power laws".

Indeed, in theory, it is common to write A -> B as the power B^A. The pair type (A,B) is also commonly written as a product (A*B).

Your first law is then written as

(A*B)^C = A^C * B^C

and is a classic type isomorphism. This can be easily generalized to tuples in the obvious way.

In the general case, where f is an arbitrary functor, I can't think of nothing else than "distribution", right now.

chi
  • 111,837
  • 3
  • 133
  • 218
  • See Chris Taylor's series starting here: http://chris-taylor.github.io/blog/2013/02/10/the-algebra-of-algebraic-data-types/ – jberryman Sep 11 '17 at 16:58
2

There is Data.Distributive which is the dual of Data.Traversable. It provides the distribute function which can be specialized e.g. as f (Stream a) -> Stream (f a) or distribute :: f (Vec n a) -> Vec n (f a). The latter example is a homogeneous variant of your family of functions.

But we can generalize Data.Distributive a bit just like lenses generalize functors. Enter Colens:

type Colens s t a b = forall f. Functor f => (f a -> b) -> f s -> t

Here is the mirror of Control.Lens.Each:

class Coeach s t a b | s -> a, t -> b, s b -> t, t a -> s where
  coeach :: Colens s t a b

instance (a~a', b~b') => Coeach (a,a') (b,b') a b where
  coeach f p = (f $ fst <$> p, f $ snd <$> p)

instance (a~a2, a~a3, b~b2, b~b3) => Coeach (a,a2,a3) (b,b2,b3) a b where
  coeach f p = ...

...

And just like with each we can iterate over tuples

each_id1 :: Applicative f => (f a, f a) -> f (a, a)
each_id1 = each id

each_id2 :: Applicative f => (f a, f a, f a) -> f (a, a, a)
each_id2 = each id

with coeach we can coiterate over tuples:

coeach_id1 :: Functor f => f (a, a) -> (f a, f a)
coeach_id1 = coeach id

coeach_id2 :: Functor f => f (a, a, a) -> (f a, f a, f a)
coeach_id2 = coeach id

This is still homogeneous, though. I don't know lens much, so can't say whether there is a heterogeneous each and the corresponding coeach.

effectfully
  • 12,325
  • 2
  • 17
  • 40