2

I was recently writing a solution for the ISBN Verifier exercise at Exercism, and when I ran this function through pointfree.io:

\c -> isDigit c || c == 'X'

I got back:

liftM2 (||) isDigit ('X' ==)

Why did pointfree.io choose liftM2 from Control.Monad instead of liftA2 from Control.Applicative?

Jeff Pratt
  • 1,619
  • 1
  • 13
  • 21
  • 3
    `pointfree.io` is a fun toy, nothing else. It's shouldn't in any way be expected to give _good_ suggestions, it only hints what's possible. – leftaroundabout Mar 29 '18 at 21:01
  • 1
    I'm surprised it chose `('X' ==)` rather than `(== 'X')`. Obviously this transposition is "safe" because `(==)` is supposed to be symmetrical, but why wouldn't it just choose an exact transformation? – amalloy Mar 29 '18 at 21:59
  • 6
    @amalloy I can't answer why that tool does it, but it is something I pay attention to myself when writing partial applications. As a rule of thumb, applying earlier arguments first is better, because carefully written functions may share more computation when given their first arguments than when dropped under a lambda. e.g. imagine if `(==)` first compared some expensive statistic about each argument, then continued with some other comparisons; then `map (foo==) bar` could be significantly more efficient than `map (==foo) bar` because the former computed `foo`'s statistic just once. – Daniel Wagner Mar 30 '18 at 00:48

1 Answers1

8

The fact is that Control.Monad is much older than Control.Applicative.

Monads were already in Haskell 98, while the paper about applicative functors was introduced in 2007. The package in Hackage exists since 2005.

Wikipedia:

Due to historical accident, applicative functors were not implemented as a superclass of Monad, but as a separate type class. It turned out that, in practice, there was very little demand for such a separation, so in 2014, it was proposed to make Applicative retroactively a superclass of Monad.

So liftM{N} are still valid.

duplode
  • 33,731
  • 7
  • 79
  • 150
Scarabyte
  • 303
  • 1
  • 5
  • 1
    This answer is entirely correct. It is perhaps worth noting that the Wikipedia explanation is rather odd: "there was very little demand for such a separation" sounds like an euphemism, as the separation served no purpose whatsoever. – duplode Mar 30 '18 at 02:24
  • 1
    @duplode, I think the explanation sounds wrong because `Applicative` was always (to the best of my knowledge) a subclass of `Functor`. It certainly was by `base-4.0.0.0`, back in 2009. – dfeuer Mar 30 '18 at 20:42
  • @dfeuer Oh wow, that's flat out wrong indeed. (I hadn't actually realised that page was also talking about `Functor`, due to a combination of late night posting and expecting to see the usual remarks about the AMP.) – duplode Mar 30 '18 at 23:04
  • 1
    @duplode, after checking through all the ancient documentation to verify that `Applicative` has been a subclass of `Functor` since its introduction in `base-4.0.0.0`, I edited Wikipedia to remove the very definite error. I think someone saw that the `Applicative` class stretched out in the original paper lacked a `Functor` superclass and got confused. – dfeuer Mar 30 '18 at 23:32
  • 1
    @dfeuer This conversation has reminded me that the *first* paragraph of that article [has also been recognised as nonsense for quite a while](https://stackoverflow.com/q/42043385/2751851). I have made a quick attempt at making it, if nothing else, correct. – duplode Mar 31 '18 at 00:32