5

ap doesn't have a documented spec, and reads with a comment pointing out it could be <*>, but isn't for practical reasons:

ap                :: (Monad m) => m (a -> b) -> m a -> m b
ap m1 m2          = do { x1 <- m1; x2 <- m2; return (x1 x2) }
-- Since many Applicative instances define (<*>) = ap, we
-- cannot define ap = (<*>)

So I assume the ap in the (<*>) = ap law is shorthand for "right-hand side of ap" and the law actually expresses a relationship between >>= , return and <*> right? Otherwise the law is meaningless.

The context is me thinking about Validation and how unsatisfying it is that it can't seem to have a lawful Monad instance. I'm also thinking about ApplicativeDo and how that transformation sort of lets us recover from the practical effects of a Monad instance for Validation; what I most often want to do is accumulate errors as far as possible, but still be able to use bind when necessary. We actually export a bindV function which we need to use just about everywhere, it's all kind of absurd. The only practical consequence I can think of the lawlessness is that we accumulate different or fewer errors depending on what sort of composition we use (or how our program might theoretically be transformed by rewrite rules, though I'm not sure why applicative composition would ever get converted to monadic).

EDIT: The documentation for the same laws in Monad is more extensive:

Furthermore, the Monad and Applicative operations should relate as follows:

pure = return
(<*>) = ap

The above laws imply:

fmap f xs  =  xs >>= return . f
(>>) = (*>)

"The above laws imply"... so is the idea here that these are the real laws we care about?

But now I'm left trying to understand these in the context of Validation. The first law would hold. The second could obviously be made to hold if we just define (>>) = (*>).

But the documentation for Monad surprisingly says nothing at all (unless I'm just missing it) about how >> should relate. Presumably we want that

a >> b = a >>= \_ -> b

...and (>>) is included in the class so that it can be overridden for efficiency, and this just never quite made it into the docs.

So if that's the case, then I guess the way Monad and Applicative relate is actually something like:

return = pure
xs >>= return . f = fmap f xs
a >>= \_ -> b = fmap (const id) a <*> b
jberryman
  • 16,334
  • 5
  • 42
  • 83
  • 7
    It's not really a law, so much as an acknowledgement that lots of existing code implement `Applicative` in terms of a pre-existing `Monad` class, instead of implementing `Applicative` from scratch and defining `Monad` in terms of it. Defining `ap = (<*>)` would break that could, as `(<*>)` would then have a circular definition. – chepner Oct 24 '17 at 14:57
  • @chepner "it's not really a law" - does "it" here refer to the comment to `ap` that I posted? I'm asking about the laws [here](http://hackage.haskell.org/package/base-4.9.1.0/docs/Control-Applicative.html#t:Applicative) starting "It it is also a Monad it should also satisfy...", and the same (actually better documented) for `Monad`. But maybe you're saying that law is a _consequence_ that Applicative instances are often defined in terms of their `Monad` instance? – jberryman Oct 24 '17 at 15:29
  • 6
    It's worth noting that there are significant Monads where the standard `ap` is a rather inefficient way to implement `<*>`. My favourite example is the vectors of a fixed length, where the `join` takes the diagonal of a square matrix: far better just to zip directly. So, the intention is that, *extensionally*, we should have `fm <*> sm = fm >>= \ f -> sm >>= \s -> return (f s)`, but to leave open the option to make `<*>` *intensionally* more efficient. – pigworker Oct 24 '17 at 16:02
  • "The context is me thinking about Validation and how unsatisfying it is that it can't seem to have a lawful Monad instance." Could you expand on what you mean by that? I personally don't find it at all unsatisfying - `Validation` _can't_ have a `Monad` instance, because then it wouldn't be `Validation`. (What I do find unsatisfying is that Haskell doesn't have [nice syntactic sugar](https://personal.cis.strath.ac.uk/conor.mcbride/pub/she/idiom.html) for `Applicative` computations. `ApplicativeDo` helps a little...) – Benjamin Hodgson Oct 24 '17 at 16:25
  • @BenjaminHodgson I've updated the question. Nice syntactic sugar is part of it, but what I'm talking about is accumulating errors as far as possible, e.g. `(f <$> x <*> y <*> z) >>= thisMightFailDependingOnInput`. We already need to do this sort of composition everywhere, we just don't use `>>=` we use `bindV`. – jberryman Oct 24 '17 at 17:21
  • @chepner I think I was unclear. I probably should have asked about the `Monad` law. That is I'm trying to understand how precisely Applicative and Monad are meant to relate – jberryman Oct 24 '17 at 17:43
  • 1
    Every Monad gives rise to an Applicative (and for that induced Applicative, `<*> = ap` will hold definitionally), but given two structures - `Monad m` and `Applicative m` - there is no guarantee that these structures agree without the two laws `<*> = ap` and `pure = return` (e.g. take the 'regular' Monad instance for lists, and the zip-list Applicative instances). While there is nothing fundamentally 'wrong' about a Monad and Applicative instance disagreeing, it would probably be confusing to most users (and so it's prohibited by the Monad laws). – user2407038 Oct 24 '17 at 18:17
  • 1
    Regarding the edit, I *definitely* wouldn't go from "law A implies law B" to "law B is the law we actually care about". – Daniel Wagner Oct 24 '17 at 18:19
  • @DanielWagner what I'm trying to get at are the real laws. It seems to me `(<*>) = ap` doesn't strictly imply anything (at least post-AMP). Presumably it's trying to express some relationship between `<*>` and the right-hand side of `ap`. Maybe I'm being pedantic. – jberryman Oct 24 '17 at 18:40
  • @user2407038 thanks, that's so obvious but really clarifies things for me, including how to think about e.g. a Monad instance for Validation. Would you mind making that an answer? – jberryman Oct 24 '17 at 18:48

2 Answers2

3

So I assume the ap in the (<*>) = ap law is shorthand for "right-hand side of ap" and the law actually expresses a relationship between >>=, return and <*> right?

It seems to me (<*>) = ap doesn't strictly imply anything (at least post-AMP). Presumably it's trying to express some relationship between <*> and the right-hand side of ap. Maybe I'm being pedantic.

Speaking pedantically, I'd say the opposite: because ap is definitionally equal to its right-hand side, saying (<*>) = ap is exactly the same as saying m1 <*> m2 = do { x1 <- m1; x2 <- m2; return (x1 x2) }. It's just the normal first step of dealing with equalities like that: expanding the definitions.

Reply to the comment:

Right, but the definition is free to change.

Then the law would change or be removed too. Just as when/if join is added to Monad the current definition will become a law instead.

it wouldn't have been possible to define it literally as ap = <*>

Do you mean it would be impossible to define ap or the law in this way?

If ap, then you are correct: it would have the wrong type. But stating the law like this would be fine.

Community
  • 1
  • 1
Alexey Romanov
  • 167,066
  • 35
  • 309
  • 487
  • Right, but the definition is free to change. But I've realized that `(<*>) = ap` as a way of documenting the law made more sense pre-AMP; it probably only has one reasonable definition, and it wouldn't have been possible to define it literally as `ap = <*>` – jberryman Oct 24 '17 at 21:02
3

Every Monad gives rise to an Applicative, and for that induced Applicative, <*> = ap will hold definitionally. But given two structures - Monad m and Applicative m - there is no guarantee that these structures agree without the two laws <*> = ap and pure = return. For example, take the 'regular' Monad instance for lists, and the zip-list Applicative instance. While there is nothing fundamentally 'wrong' about a Monad and Applicative instance disagreeing, it would probably be confusing to most users, and so it's prohibited by the Monad laws.

tl;dr The laws in question serve to ensure that Monad and Applicative agree in an intuitively obvious way.

user2407038
  • 14,400
  • 3
  • 29
  • 42