5

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 (<*>).

Zhiltsoff Igor
  • 1,812
  • 8
  • 24
  • 2
    Side note to your side note: functors such that `(<**>) = flip (<*>)` are known as commutative applicative functors (or commutative monads, if they happen to be monads as well). – duplode Jul 31 '20 at 23:20
  • 1
    possibly better description could be "A variant of `<*>` with the arguments' *roles* reversed." – Will Ness Aug 14 '20 at 09:10
  • @WillNess agreed. Truth be told, I would have gone even further writing some kind of a relation between `<*>` and `<**>` down, like this one: `xf <**> ff = (&) <$> xf <*> ff`. It would also add more clearance to the definition, as the right hand **IS** the definition, just in terms of `<*>`, not `liftA2`: `liftA2 f xf yf = f <$> xf <*> yf`. – Zhiltsoff Igor Aug 14 '20 at 09:17
  • 1
    some like `liftA2` better; `(<*>) = liftA2 ($)` and `(<**>) = liftA2 (&)` are perfectly nice and clear. and short. :) – Will Ness Aug 14 '20 at 09:39

3 Answers3

7

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.

Li-yao Xia
  • 31,896
  • 2
  • 33
  • 56
Ackdari
  • 3,222
  • 1
  • 16
  • 33
  • 1
    yes! Functors are generalized loops `[ f x | x <- xs]`; Applicatives are generalized *nested* loops `[ (x,y) | x <- xs, y <- ys]`; Monads are generalized *dynamically created* nested loops `[ (x,y) | x <- xs, y <- k x]`. – Will Ness Aug 14 '20 at 09:21
  • or we could choose another base, with `fmap f xs = [ f x | x <- xs]`, `ap xs ys = [ x y | x <- xs, y <- ys]`, `join xs = [ y | x <- xs, y <- x]`. all these written with MonadComprehensions of course. – Will Ness Aug 14 '20 at 12:58
7

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 (&)

Iceland_jack
  • 6,848
  • 7
  • 37
  • 46
6

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))
Li-yao Xia
  • 31,896
  • 2
  • 33
  • 56
  • I guess that is what I used proving the equivalence of `<**>` and `flip (<*>)` for the reader. – Zhiltsoff Igor Jul 31 '20 at 14:01
  • 1
    Perhaps use `($)`? – dfeuer Aug 01 '20 at 02:26
  • @dfeuer well, as far as I know, `$` cannot be used in `Control.Applicative`. I guess it’s up to us whether we want to follow this rule outside the library or not. – Zhiltsoff Igor Aug 01 '20 at 06:39
  • @ZhiltsoffIgor, is that some sort of joke? There might (I don't know) be issues using `($)` in `GHC.Base`, but I can't imagine it's a problem elsewhere. – dfeuer Aug 01 '20 at 14:36
  • @dfeuer From Control.Applicative: >NOTA BENE: Do NOT use `($)` anywhere in this module! The type of `($)` is slightly magical (it can return unlifted types), and it is wired in... You can view the full text if you follow the link I enclosed with my question in the first line (the first note in the module). The authors warn us about not using `$` right under the definition of `<**>` once again. – Zhiltsoff Igor Aug 01 '20 at 14:44
  • @ZhiltsoffIgor, that warning is in `GHC.Base`, not `Control.Applicative`. Note that the `Applicative` typeclass and `<**>` are defined in `GHC.Base`. There's certainly no need to worry about it anywhere outside that module. – dfeuer Aug 01 '20 at 17:15
  • @dfeuer you are right, sorry - did mistook the link for some reason. I edited the post. Nonetheless, the module in which the operators are defined contains the warning :). – Zhiltsoff Igor Aug 01 '20 at 17:18