Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does <$> and <*> take input in an order opposite of >>=?

Tags:

I understand the reasoning behind <$>'s type signature, as it's just an infix version of fmap, but comparing it to >>='s type signature it makes a lot less sense to me.

Let's first establish what I mean by that.

(>>=) :: Monad m => m a -> (a -> m b) -> m b
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
(<$>) :: Functor f => (a -> b) -> f a -> f b

Looking at the type signatures we can see that >>= takes a value on the left, and a function on the right, which makes a lot of sense if you consider its chaining property: foo >>= bar >>= baz

Which leads me to wonder, why don't <*> and <$> do that too? you can't write foo <*> bar <*> baz because it would require the output of foo <*> bar to be a function, not a value.

I know that <**> and =<< exist that both flip the order of the parameters, allowing me to do something like:

Just 4 <**> pure (+3) <**> pure (*2) >>= (\x -> Just (x-3)) 

Which could have been beautifully reduced to:

Just 4 <$$> (+3) <$$> (*2) >>= (\x -> Just (x-3))

If <$$> had existed, or if the parameter order of <$> and <*> had been reversed.

Another thing that makes me wonder why the difference exists, is that it makes it harder for newcomers to get used to and/or remember whether it's the function, or the value that comes first, without having to look it up.

So why is it that in the cases of <*> and <$> it's fn op val but with >>= it's val op fn?

like image 901
Electric Coffee Avatar asked Sep 24 '14 11:09

Electric Coffee


2 Answers

Don't let monads get in the way here. Think about application.

Compare:

(<$>) :: Functor f => (a -> b) -> f a -> f b

and

($) :: (a -> b) -> a -> b

You can see there is a connection between regular application and application under a functor.

Trivia: there have been proposals to use brackets to overload whitespace (application) so that we can write:

(| f a1 .. an |)

instead of

pure f <*> a1 <*> .. <*> an
like image 193
Don Stewart Avatar answered Oct 07 '22 07:10

Don Stewart


The answer to "why does it take parameters in this order" is basically "because". Whoever defined these functions thought that was the best way. (And it probably wasn't the same person in each case.) I will, however, offer some examples:

Suppose we have a parsing monad of some kind. Suppose also that we have defined

 data Foobar = Foobar X Y Z

 parseFoo :: Parser X
 parseBar :: Parser Y
 parseBaz :: Parser Z

Then we can write

 parseFoobar :: Parser Foobar
 parseFoobar = do
   foo <- parseFoo
   bar <- parseBar
   baz <- parseBaz
   return (Foobar foo bar baz)

Or, explicitly,

parseFoobar =
  parseFoo >>= \ foo ->
  parseBar >>= \ bar ->
  parseBaz >>= \ baz ->
  return (Foobar foo bar baz)

Now let's write that applicative-style:

parseFoobar = return Foobar <*> parseFoo <*> parseBar <*> parseBaz

or, alternatively,

parseFoobar = Foobar <$> parseFoo <*> parseBar <*> parseBaz

If we suppose that <**> = flip <*> exists (and has the correct associativity), then we have

parseFoobar = parseBaz <**> parseBar <**> parseFoo <**> return Foobar

That just looks strange. With the function at the end, and the arguments in reverse order? Why would you want to write it that way? (Note that any effects are also in reverse order.)

In the monadic version, effects happen top-to-bottom. In the applicative version, effects happen left-to-right. That seems "natural".

like image 21
MathematicalOrchid Avatar answered Oct 07 '22 07:10

MathematicalOrchid