No, this is precisely the extra power that Monad brings compared to Applicative. A monadic, or applicative, value of type f a
can be thought of as being two parts: the "effects" (stuff happening in the f
data structure), and the "value" (stuff happening in the values of type a
). With Applicative, it is impossible for the effects to depend on the value, because the functions in Applicative give you no way to weave a function's results (the value) back into the effect. The (>>=)
function in Monad gives you this power; join
is equivalently powerful.
The key is that the signature for (>>=)
contains a function that looks like (a -> m b)
: you get to look at a pure value (a
), and choose an effect (m b)
based on that. Compare
(>>=) :: Monad m => (a -> m b) -> m a -> m b
to
fmap :: Functor f => (a -> b) -> f a -> f b
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
The last two accept only functions that operate entirely in the value realm: (a -> b)
, and so cannot determine structure/effect based on values.
Of course you can still desugar this do
notation, but you will have to use monadic operations in the result:
doSomething'' n =
f n >>= (\a ->
g a >>= (\b ->
h b >>= (\c ->
pure (a, b, c))))