8

There are a lot of functions that accomplish the same thing, but using Applicative vs Monad definitions.

Some examples are:

  • (<*>) vs ap
  • pure vs return
  • (*>) vs (>>)
  • traverse vs mapM
  • sequenceA vs sequence
  • liftA vs liftM
  • liftA2 vs liftM2
  • etc.

And for Alternative vs MonadPlus:

  • empty vs mzero
  • (<|>) vs mplus

In the scenario that prompted this question, I wanted to traverse/mapM where the Applicative/Monad is [].

In general, when it is known that the result will be the same, is it better style to use the Applicative ones or the Monad ones? E.g., is one more readable than another, or maybe faster than the other?

Edit (in response being suggested as a duplicate): Personally, I find these to be equally readable. I am in no way asking about maintainability, as these can be used interchangeably, and preferring one over another would have no impact on the code structure. I'm really wondering if one is more idiomatic, or more efficient, than the other.

zbw
  • 191
  • 3
  • Possible duplicate of [How would you know if you've written readable and easily maintainable code?](https://softwareengineering.stackexchange.com/questions/141005/how-would-you-know-if-youve-written-readable-and-easily-maintainable-code) – gnat Jun 29 '17 at 18:05
  • The general rule of thumb I follow – although I don't have a good source for this, which is why this isn't an answer – is "always use the `Applicative` ones". In a hypothetically correctly-having-`Applicative`-from-the-beginning Haskell, we'd *only* have `(<*>)`, `traverse`, etc. (Also: `liftA` is the same as `fmap` / `(<$>)`, which is the real thing to use there.) – Antal Spector-Zabusky Jun 29 '17 at 19:26
  • 1
    @gnat I'm curious, why do you think this question is a duplicate? To me, it doesn't seem to be even be superficially related to the "duplicate" you identified. – Nathan Davis Aug 21 '17 at 16:11

1 Answers1

11

For both efficiency and generality, you should typically prefer the most general operators. So you should use Applicative operators in preference to Monad, and Alternative to MonadPlus.

The efficiency difference is often not significant, so if you already have a Monad/MonadPlus constraint, you should feel free to use any of the relevant operators to produce the nicest looking code.

The ApplicativeDo extension can allow you to get the benefit of do-notation without a Monad requirement. I recommend the Haxl paper for an non-trivial example where there is a quite significant performance difference between Applicative and Monad (and was a motivator for implementing ApplicativeDo notation).

Nathan Davis
  • 398
  • 3
  • 8
Derek Elkins left SE
  • 6,591
  • 2
  • 13
  • 21
  • 1
    I agree with this answer, except that often, using the most general operator gives *less* efficiency, because the particular type may have special properties that make the algorithm faster for it (trivial example: built-in `[a]` versus length-tagged `([a], Int)` have, respectively, `O(n)` and `O(1)` length functions). – Ryan Reich Oct 16 '17 at 17:52
  • I'm not suggesting using the most general operator in all cases; I'm only suggesting it in the context of choosing between operations in the `Monad`, `Applicative`, `Functor` hierarchy and closely related classes. The idea is not to pay for something you don't use. In the general case, a less contrived example is `nub` versus `nubOrd`. For more elaborate functions built from the `Monad`/`Applicative` operations, the extra structure can allow for better algorithms, e.g. a generate-and-test algorithm where you filter as you go, but the implementation difference is more than swapping operators. – Derek Elkins left SE Oct 16 '17 at 21:11