Applicatives: <$> vs. pure and <*>

This needs to be equivalent. Indeed, in the documentation of the Applicative typeclass, we read:

As a consequence of these laws, the Functor instance for f will satisfy

fmap f x = pure f <*> x

Since the (<$>) :: Functor f => (a -> b) -> f a -> f b is an:

An infix synonym for fmap.

It thus holds that:

f <$> x = pure f <*> x

The two can thus be used to achieve the same. Since f <$> x is however shorter, and might be faster (since (<*>) needs to deal with all f as), it is probably advisable to use (<$>)). Furthermore as @chepner says, the default implementation of liftA2 is liftA2 f x = (<*>) (fmap f x), so this uses fmap (so <$>) as well.


I'd just like to chime in briefly here, because I hold many unpopular opinions and this is one of them and you asked so nyaaaaah.

Admitted: the community appears to more or less agree that f <$> x <*> y <*> z style is better. But I actually prefer pure f <*> x <*> y <*> z. I find it relatively common that lines written in applicative style tend towards being longish, since each argument is often itself a call to a function, so:

fancyParser = FancyConstructor <$> fooParserWith 7 blag <*> barParserSep "hi" (sizzleParser pop) <*> bazParser
-- OR
fancyParser = pure FancyConstructor <*> fooParserWith 7 blag <*> barParserSep "hi" (sizzleParser pop) <*> bazParser

For readability, I often split the arguments onto their own lines; I find the visual separation makes it clearer where the argument boundaries are, gives my eyes a bit of a rest, and makes it less likely that the line will wrap in an ugly way:

fancyParser = FancyConstructor
    <$> fooParserWith 7 blag
    <*> barParserSep "hi" (sizzleParser pop)
    <*> bazParser
-- OR
fancyParser = pure FancyConstructor
    <*> fooParserWith 7 blag
    <*> barParserSep "hi" (sizzleParser pop)
    <*> bazParser

In this form, I think it's quite clear why I prefer pure/<*>: it makes for a completely consistent line beginning. This consistency is visually appealing, for one. But more importantly, when I inevitably refactor FancyConstructor to rearrange the order of fields, or insert a new field at the beginning, I can use line-wise editing commands with complete impunity. Certain regex searches become slightly easier as well. It only eliminates a little bit of friction... but eliminating small frictions is one important part of doing programming in the large.

P.S. Other unpopular examples of ways to get consistent line formats that I've found handy:

-- orthodox
foo a b c d
    = f
    . g
    . h

-- dmwit
foo a b c d = id
    . f
    . g
    . h

-- orthodox
foo a b c d =
    [ x
    , y
    , z
    ]

-- dmwit
foo a b c d = tail [undefined
    , x
    , y
    , z
    ]

-- orthodox
bad = die
    $  "some kind of "
    <> "error that is best described "
    <> "on multiple lines"

-- dmwit
bad = die $ mempty {- or "", if appropriate -}
    <> "some kind of "
    <> "error that is best described "
    <> "on multiple lines"

-- orthodox
foo
    :: arg1
    -> arg2
    -> result

-- dmwit
foo ::
    arg1 ->
    arg2 ->
    result

-- orthodox
foo a b c d =
    [ t
    | u <- v
    , w <- x
    , y <- z
    ]

-- dmwit
foo a b c d =
    [ t | _ <- [()]
    , u <- v
    , w <- x
    , y <- z
    ]