join
is defined along with bind
to flatten the combined data structure into single structure.
From type system view, (+) 7 :: Num a =>
how to get some intuition about it instead of just relying on type system?
I'd rather say that relying on the type system is a great way to build a specific sort of intuition. The type of join
is:
join :: Monad m => m (m a) -> m a
Specialised to (->) r
, it becomes:
(r -> (r -> a)) -> (r -> a)
Now let's try to define join
for functions:
-- join :: (r -> (r -> a)) -> (r -> a)
join f = -- etc.
We know the result must be a r -> a
function:
join f = \x -> -- etc.
However, we do not know anything at all about what the r
and a
types are, and therefore we know nothing in particular about f :: r -> (r -> a)
and x :: r
. Our ignorance means there is literally just one thing we can do with them: passing x
as an argument, both to f
and to f x
:
join f = \x -> f x x
Therefore, join
for functions passes the same argument twice because that is the only possible implementation. Of course, that implementation is only a proper monadic join
because it follows the monad laws:
join . fmap join = join . join
join . fmap return = id
join . return = id
Verifying that might be another nice exercise.