<**> is a variant of <*> with the arguments reversed. What does "reversed" mean?

Viewed 159

In GHC.Base the description of <**> runs:

A variant of <*> with the arguments reversed.

It is widely known that "reversed" in that case does not mean "flipped" as:

GHCi> [1, 2, 3] <**> [(^2), (+1)]
[1,2,4,3,9,4]
GHCi> [(^2), (+1)] <*> [1, 2, 3]
[1,4,9,2,3,4]

So, what does "reversed" mean?


Side note: there are applicative functors which have (<**>) = flip (<*>). For example, here is my proof for the reader ((->) e):

(->) e: f <**> g =
    = liftA2 (flip ($)) f g =
    = (flip ($) <$> f) <*> g =
    = \e -> ((flip ($) . f) e) (g e) =
    = \e -> flip ($) (f e) $ (g e) =
    = \e -> (g e) $ (f e) =
    = \e -> g e (f e) =
    = g <*> f. => (<**>) = flip (<*>).

3 Answers

If we stay at the example of lists we can see through your examples how <**> behaves reverse.

The expression as <**> fs means something like

foreach a in as {
    foreach f in fs {
        add (f a) to result;
    }
}

and fs <*> as means something like

foreach f in fs {
    foreach a in as {
        add (f a) to result
    }
}

So as <**> fs results in [f1(a1), f2(a1), ..., fn(a1), f1(a2), ..., fn(a2), ...]

And fs <*> as result in [f1(a1), f1(a2), ... , f1(am), f2(a1), ...]

So the order of the loops is reversed.

I recently added do-notation to the base documentation which makes it easier to compare <*> and <**>, notice how both of them run left-to-right and both of them return f a:

  fs <*> as
=
  do f <- fs
     a <- as
     pure (f a)

and

  as <**> fs
=
  do a <- as
     f <- fs
     pure (f a)

It is known and codified (Control.Applicative.Backwards) that applicatives can be run backwards , I have to cut this answer short. Li-yao Xia's answer with liftA2 ($) and liftA2 (&)

One way to illustrate it symbolically is to compare their expressions in terms of liftA2:

(<*>)  = liftA2 (\f x -> f x)
(<**>) = liftA2 (\x f -> f x)
       = liftA2 (flip (\f x -> f x))
Related