Generally, a "computation in a monad" means not just a function returning a monadic result, but such a function used inside a do
block, or as part of the second argument to (>>=)
, or anything else equivalent to those. The distinction is relevant to something you said in a comment:
"Computation" occurs in func f, after val extracted from input monad, and before result is wrapped as monad. I don't see how the computation per se is "in" the monad; it seems conspicuously "out" of the monad.
This isn't a bad way to think about it--in fact, do
notation encourages it because it's a convenient way to look at things--but it does result in a slightly misleading intuition. Nowhere is anything being "extracted" from a monad. To see why, forget about (>>=)
--it's a composite operation that exists to support do
notation. The more fundamental definition of a monad are three orthogonal functions:
fmap :: (a -> b) -> (m a -> m b)
return :: a -> m a
join :: m (m a) -> m a
...where m
is a monad.
Now think about how to implement (>>=)
with these: starting with arguments of type m a
and a -> m b
, your only option is using fmap
to get something of type m (m b)
, after which you can use join
to flatten the nested "layers" to get just m b
.
In other words, nothing is being taken "out" of the monad--instead, think of the computation as going deeper into the monad, with successive steps being collapsed into a single layer of the monad.
Note that the monad laws are also much simpler from this perspective--essentially, they say that when join
is applied doesn't matter as long as the nesting order is preserved (a form of associativity) and that the monadic layer introduced by return
does nothing (an identity value for join
).