MonadCont under the hood

From HaskellWiki

This tutorial is a response to the following Stack Overflow question. There's a short but useful description of Cont and MonadCont in the Control.Monad.Cont documentation, but it doesn't really describe how the continuation monad works. This is an attempt at much more detailed explanation of what Cont and MonadCont are doing, particularly below the hood.

This tutorial assumes a working knowledge of Haskell, though of course it doesn't assume that you understood the implementation of

1 Continuations and the Cont monad

Continuations are functions that represent "the remaining computation to do." Their representation here is

a -> r

, which is simply a function that takes some value produced by the current computation, of some type

a

, and returns the final result of type

r

from it.
The type

Cont r a

(instances of which I will, in this tutorial, refer to as Cont objects) represents a continuation-passing-style function that takes a single continuation as its only input. In other words, its guts are a function that:

takes a continuation as an argument

does whatever it needs to do

produces a value of type

r

at the end, presumably by invoking the continuation.

Note that whatever it needs to do, i.e. whatever values it needs to be able to use to do its thing, must already be bound up into the

Cont

object. So, generally, we won't be dealing with

Cont

objects directly, but with functions that can ultimately produce one.

2 Sequencing Continuation-Style Computations

2.1 Applicative Sequencing

Cont

objects can be chained together, so that the continuation you pass in threads through the guts of all the

Cont

objects in the chain before it's finally invoked. The way they chain is the way

Cont

works: each object in the chain invokes a continuation that has the next object's computation prepended to the final continuation. Let's say we have a chain of

Cont

objects

F1 -> F2 -> F3

, and let's say you had a continuation

C3

that you want to pass to the chain. Then:

F3

needs to invoke

C3

when it's done

F2

needs to invoke a continuation

C2

that will invoke

F3

, which will invoke

C3

.

F1

needs to invoke a continuation

C1

which will invoke

F2

, which will invoke

F3

, which will invoke

C3

.

What I've described so far is the applicative operation of continuations.

2.2 Extending to Monad

With the

Monad

operation there's an extra wrinkle: we allow for the value of one computation to affect which

Cont

object gets invoked next. In this world:

return

takes a value and produces a

Cont

object that just passes that value to its continuation.

bind

takes a

Cont

object, and a function that produces another

Cont

object given a value from the first, and chains them together into one

5 Understanding MonadCont and callCC

One final extension to this which is frequently used is the

MonadCont

class, which provides a

callCC

operation.

callCC

creates a

Cont

object that invokes the function it's given, but provides a second continuation to that function that can be invoked to "break out" of the computation and simply pass a value to the continuation that was active when

callCC

was invoked. This function's operation is definitely easier to understand by seeing it in action. Evaluate the following code, replacing the corresponding functions above: