Maybe monad: where is the join and nested Nothing

The monad is defined as a triple, two of the triple are natural transformations. The flatmap is defined directly from the natural transformation and fmap. So there is fmap inside every flatmap. The flatmap should not be very different from fmap.

When I looked at the definition of bind function of Maybe monad and try to look at in perspective of join. I found I can't see the join part of the definition, because it's hid and blended. Here is the definition of Maybe monad:

instance Monad Maybe where
  return = Just
  Nothing >>= _ = Nothing
  Just a >>= f = f a

The bind, aka >>= can be defined as join . (fmap f). The f is the Kleisli arrow. The definiton of Maybe functor and fmap:

instance Functor Maybe where
  -- fmap :: (a -> b) -> Maybe a -> Maybe b
  fmap _ Nothing = Nothing
  fmap f (Just x) = Just (f x)

You can blend the f, fmap and join together, the result is the same as bind, the process will be like this

  Nothing >>= _ = join . (Nothing Nothing) = Nothing
  Just a >>= f = join . (Just (f a)) = f a

Which just strip one level of Just or Nothing, the result is exactly the same as current definition of bind function of Maybe monad. In the case of Just, even though the function body is simply apply the f to the value inside Just a, behind the scene the result of f a is wrapped with Just and then the join strips it away, the two forms are equivalent and the final result is the same, very reasonably, the shorter form is used in the definition of bind.

There is no nested Nothing in Haskell, but semantically and logically, there is. Check this out

Prelude> f x = Just x
Prelude> fmap f Nothing
Prelude> :t fmap f Nothing
fmap f Nothing :: Maybe (Maybe a)

When you have a function with type: a -> Just a, the fmap f will have the type: Maybe a -> Maybe (Maybe a) because both the input and output type are lifted, even the fmap f didn't apply the f because the Maybe functor is Nothing, the type is still nested. You should think of it as to pretend the f is applied and returned Nothing and fmap will wrap it again with Nothing, so there is two level of Nothing, but it's also equivalent to a single Nothing. Of course, according to the definiton of fmap, the wrap never happened, but it's OK to think about it in this way if you want to understand it in perspective of join which is a better perspective to understand a monad, it tells you how the bind function end up looking like the way it is, not just a confusing final result. Understanding how a final form is deduced is much better than just memorizing the form itself.