Can join be defined in terms of bind?
TL;DR answer: Yes.
join ∷ (Monad m) ⇒ m (m a) → m a
join = (=<<) id
The longer answer:
To add some subtleties that have yet to be mentioned I'll provide a new answer, starting by expanding upon Lee's answer, because it is worth noting that their answer can be simplified. Starting with the original:
join ∷ (Monad m) ⇒ m (m a) → m a
join m = m >>= id
One can look for an Eta conversion (η-conversion) opportunity to make the function definition point-free. To do this we want to first rewrite our function definition without the infix >>=
(as would likely be done if we were calling >>=
by the name bind
in the first place).
join m = (>>=) m id
Now observe that if we use the flip
function, recalling:
-- defined in Data.Function
-- for a function of two arguments, swap their order
flip ∷ (a → b → c) → b → a → c
flip f b a = f a b
One may now use flip
to put the m
in position for an η-reduction:
join m = (flip (>>=)) id m
Applying the η-reduction:
join = (flip (>>=)) id
Noticing now that flip (>>=)
can be replaced with (=<<)
(defined in Control.Monad
):
join = (=<<) id
Finally we can see shorter, point-free definition:
join ∷ (Monad m) ⇒ m (m a) → m a
join = (=<<) id
Where (=<<)
has type:
(=<<) ∷ ∀ (m ∷ * -> *) a b. (Monad m) ⇒ (a → m b) → m a → m b
which in the process gets instantiated to:
(=<<) ∷ (m a → m a) → m (m a) → m a
Additionally, one may also notice that if we put the code above back into infix form, the flip
becomes implicit, and we get the same final answer as Ben does:
join = (>>= id)