Now when you look at it, f(a -> b)
can be written as (a -> b)
No. It can't. Your intuition is (dangerously) far off at this point. That's like saying a hammer is perfect for driving screws in, since it already works for a nail*. You can't simply drop f
here, it's part of the type**.
Instead, let's get the facts straight. An Applicative
has three associated functions, counting Functor
's fmap
:
fmap :: Functor f => (a -> b) -> f a -> f b
pure :: Applicative f => a -> f a
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
Here's another fact: you can define bind ((>>=)
) in terms of join
and vice versa:
join :: Monad m => m (m a) -> m a
join k = k >>= id
(>>=) :: Monad m => m a -> (a -> m b) -> m b
k >>= f = join (fmap f k)
are the implementations of join and bind you provided here part of the Monad definition, or are only join and bind signatures part of the Monad definition? [...] So now I ask myself why would they bother.
Those aren't the official definitions of course, since they would never terminate. You have to define (>>=)
for your type if you want to make it a a monad:
instance Monad YourType where
k >>= f = ...
Also, your join definition uses id which is not in the Monad interface, why is it mathematically legitimate?
First of all, id :: a -> a
is defined for any type. Second, the mathematical definition of a monad is actually via join
. So it's "more"*** legitimate. But most important of all, we can define the monad laws in terms of join
(exercise).
If we created join
via Applicative
, we could also create bind. If we cannot create join
via Applicative
methods, neither can we derive bind
. And join
's type actually makes it obvious that we cannot derive it from Applicative
:
join :: Monad m => m (m a) -> m a
-- ^ ^ ^
Join is able to drop one of the m
layers. Let's check whether it's possible to do the same in the other methods:
fmap :: Functor f => (a -> b) -> f a -> f b
^ ^
0 here 1 here
pure :: Applicative f => a -> f a
^ | ^
0 here | 1 here
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
^ ^
1 here 1 here
The answer is no: none of the tools we're given by Applicative
enables us collapse multiple m
's into a single one. And that's also what is written in the Typeclassopedia right after the cited paragraph in the other question:
To see the increased power of Monad from a different point of view, let’s see what happens if we try to implement (>>=)
in terms of fmap
, pure
, and (<*>)
. We are given a value x
of type m a
, and a function k
of type a -> m b
, so the only thing we can do is apply k
to x
. We can’t apply it directly, of course; we have to use fmap
to lift it over the m
. But what is the type of fmap k
? Well, it’s m a -> m (m b)
. So after we apply it to x
, we are left with something of type m (m b)
—but now we are stuck; what we really want is an m b
, but there’s no way to get there from here. We can add m
’s using pure
, but we have no way to collapse multiple m
’s into one.
Note that join
doesn't make it possible to get rid of m
completely, that would be a total extraction, and—depending on some other functions—a feature of a comonad. Either way, make sure that you don't let your intuition go astray; trust and use the types.
* That comparison is a little bit bad, because you could actually try to dive a screw in with a hammer into a piece of wood. So think of a plastic screw, a rubber hammer and a carbon steel plate you want to drive the nail in. Good luck.
** Well, you can drop it, but then the type changes drastically.
*** Given that (>>=)
and join
are equivalent of power and any (>>=)
using formula can be transformed to one only using join
, they are of course both mathematical sound.