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
?
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
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".
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With