Well, I don't know if your lambdas are continuations in the strictest sense of the term, but they also look similar to this concept in my eye.
But note that if they are continuations, then the desugared monadic code is already written in continuation passing style (CPS). The usual notion of a control operator that "captures" a continuation is based on direct-style programs; the "captured" continuation is only implicit in the direct-style program, but the CPS transformation makes it explicit.
Since the desugared monadic code is already in CPS or something like it, well, maybe a way to frame your question is whether monadic code can express some of the control flow tricks that CPS code can. Typically, those tricks boil down to the idea that while under the CPS regime it is conventional for a function to finish by invoking its continuation, a function can choose to replace its continuation with another of its choosing. This replacement continuation can be constructed with a reference to the original continuation, so that it can in turn "restore" that original one if it chooses. So for example, coroutines are implemented as a mutual "replace/restore" cycle.
And looked at in this light, I think your answer is mostly no; CPS requires that in foo >>= bar
, foo
must be able to choose whether bar
will be invoked at all, and foo
must be abble to supply a substitute for bar
, but (>>=)
by itself does not offer a mechanism for foo
to do this, and more importantly, (>>=)
is in control of the execution flow, not foo
. Some specific monads implement parts or all of it (for example, the Maybe
monad allows foo
to forego execution of bar
by producing a Nothing
result), but others don't.
Closest I could get is to forego (>>=)
and use this instead:
-- | Execute action @foo@ with its "continuation" @bar@.
callCC :: Monad m => ((a -> m b) -> m b) -> (a -> m b) -> m b
foo `callCC` bar = foo bar
Here foo
can choose whether bar
will be used at all. But notice that this callCC
is really just ($)
!