Sunday, December 30, 2012

The Continuation Monad

The continuation monad is one of the least appreciated monads and in this post I hope to motivate when to use it. This post will first motivate continuations in general and then motivate them in their specific capacity as monads.

Continuations

A Haskell continuation has the following type:

newtype Cont r a = Cont { runCont :: (a -> r) -> r }

A continuation takes a function of type (a -> r) and generates an r, where r can sometimes be a fixed value like Int or IO ().

For example, I might write a long-running process that spawns an action every time the user enters a line of input:

onInput :: (String -> IO ()) -> IO ()
-- i.e. Cont (IO ()) String
onInput f = forever $ do
str
You will recognize this idiom if you've ever used frameworks with callbacks. We supply the framework with a function (i.e. a continuation) and the framework uses that function to do its job.

"Complete me Later"

You generally use continuations when you are programming something, but you want somebody else to complete it. Common reasons include:

You are programming a framework with callbacks that users supply

You are defining a custom map engine for game players to program

You are lazy

I'll use the following hypothetical code segment as an example:

unitAttack :: Target -> IO ()
unitAttack target = do
swingAxeBack 60
valid
Let's imagine you have to package up and compile this code for somebody else (say, a fellow colleague) to use later, but it won't compile yet because you still have the unspecified ??? function. What do you do?

Like all good programming, the best solution is the laziest one. We punt and take the incomplete behavior as a parameter so that whoever finishes the function later on can complete the function by passing the specified behavior in:

unitAttack :: Target -> (Target -> IO ()) -> IO ()
unitAttack target todo = do
swingAxeBack 60
valid
Problem solved! Notice how the right hand side of the type signature resembles the shape of our Cont type. If we just add a newtype, we can wrap it in Cont ourselves:

unitAttack :: Target -> Cont (IO ()) Target
unitAttack target = Cont $ \todo -> do
swingAxeBack 60
valid
... or, even better, we can use ContT instead. The benefit of ContT is that it is also a monad transformer, which comes in handy. ContT has the exact same Monad instance as Cont, so they are otherwise interchangeable:

Fortunately, there is a clean and general solution. Just define a data type that wraps both possible arguments in a sum type, and just define a single continuation that accepts this sum type:

data Hole = Swing Int | Attack Target
unitAttack :: Target -> ContT () IO Hole
unitAttack target = ContT $ \k -> do
k (Swing 60)
valid
Each constructor acts as a place-holder that signals to the continuation which hole it is currently filling. Then somebody else can continue where we left off and just write:

This trick generalizes to n holes with variable arguments per hole. Just define a type with n constructors, one for each hole, where each constructor stores whatever arguments that particular continuation will need:

data Hole = Hole1 Arg1 Arg2 | Hole2 | Hole3 Arg3 | Hole4

Algebraic Data Types

I want to digress for a moment to talk about algebraic data types. If you are not interested, skip to the next section.

It turns out we can elegantly derive the above trick for multiple holes. Type algebra says that if we squint then we can translate the following type constructors to algebraic operators and derive equivalent types from simple algebraic manipulations:

Either a b <=> a + b
(a, b) <=> a * b
a -> b <=> b ^ a

That means that if we have a function with two continuations:

(a1 -> r) -> ((a2 -> r) -> r)

... we just translate it to the equivalent algebraic expression:

(r ^ (r ^ a2)) ^ (r ^ a1)

... and then we can derive equivalent representations just by using the rules of algebra:

This is a Kleisli arrow! That means we can compose it with our previous Kleisli arrow:

unitAttack >=> halfAssedCompletion :: Target -> ContT () IO Int

This composition substitutes in halfAssedCompletion for each hole we left in the unitAttack function. However, halfAssedCompletion left smaller Int holes of its own that somebody else now has to finish up.

Notice how now we originally needed a handler of type:

handler :: Target -> IO ()

... but now we only need a smaller handler of type:

newHandler :: Int -> IO ()

... in other words, halfAssedCompletion acts as an intermediary that transforms handlers of type (Int -> IO ()) into handlers of type (Target -> IO ()).

The Cont monad is all about chaining these kinds of partial completions together until all the holes are finally filled. You could use this abstraction to complete a project in stages and seamlessly hand off work from person to person whenever circumstances require a change in maintainer before completing the project. Alternative, you can use this to condense the callback API of a framework into a single point of entry.

The Kleisli Category

Earlier I said that the key to a monad is its Kleisli arrows. The reason why is that Kleisli arrows are morphisms in the Kleisli category, where (>=>) is Kleisli arrow composition:

Things that obey these laws have nice properties. For example, it guarantees that you can reason about each Kleisli arrow in a composition chain in isolation. Each Kleisli arrow's behavior is completely determined by its input (i.e. domain) and output (i.e. codomain). So let's think about how that modularity translates to the Cont Kleisli category.

When you switch maintainers, you don't have to give the next maintainer a bunch of holes sprawled out over a large code base like this:

largeProgram = do
...
x
Instead you can unify all the holes using a single callback that accepts a single type (the "codomain") unifying all the holes you left:

largeProgram :: () -> ContT () IO Hole
largeProgram () = ContT $ \k -> do
...
x
This give the next person a single point of entry to continue from, because now they only have to write a Kleisli arrow that handles a single Hole input which encompasses all the previous holes:

Then you just use Kleisli composition to connect your code contribution:

largeProgram >=> nextContribution

This cleanly modularizes the first person's contribution so that you can hermetically seal it off from subsequent contributions. By repeating this process, each subsequent contribution to the code base becomes its own modular and composable Kleisli arrow, cleanly separated from other contributions:

This is why frameworks and game custom map makers all use continuations to delimit the interface between the company's code and the user's code. The continuation monad is all about establishing air-tight code boundaries, both internally within a project, and externally for user facing APIs. This lets you isolate responsibilities if you can separate each code contribution into its own Kleisli arrow.

Callback Hell

Frameworks are the canonical example of separating responsibilities, where the framework writer provides some code, but the user is expected to fill in the gap with callbacks of their own. This often results in callback hell in frameworks that take this principle to the extreme, like Node.js.

But it doesn't have to be that way. The continuation monad teaches us that we can always condense a sprawling API filled with callbacks into a single callback that takes a single argument. Even better, we get monadic syntactic sugar for composing multiple layers of callbacks.

I'll use the GLUT package as an example, which requires several callbacks like:

It says the return type is polymorphic, meaning that there is no hole left to fill. The above function just inserts return () in all holes and calls it a day. We can even prove a chain of continuations is done if its final return value type-checks as Void, the empty type:

run only accepts completed programs that have no holes left. We can use run for our previous GLUT example, since the final user callback handler leaves no unfinished holes:

run ((glut >=> userCallbacks) ()) :: IO ()

Conclusion

I hope this post inspires people to use the continuation monad to structure and modularize code completion boundaries. The continuation monad naturally arises at the boundaries between programmers and cleanly abstracts away callback hell into a simple and uniform interface with a single entry point.

Something is bothering me here at about the point where the second hole appears. It's fine if you use the ContT constructor directly:

newtype ContT r m a = ContT { runContT :: (a -> m r) -> m r }

because here the continuation is represented as an ordinary function *that returns*, so it can be called multiple times, eg once with (Swing 60) and again with (Attack Target).

However, is using the ContT constructor like this really what you're supposed to do? If the constructor is hidden, then the standard way to muck about with the continuation is to use callCC, which leads me to write something like this:

BUT, when you do this, the first call to k aborts the rest of the computation--k never returns and the target is never attacked. Surely, to get the effect you are after without breaking the continuation monad abstraction is to use composable or delimited continuations? I'm only just learning about delimcc and all that so I'm not sure if I've got the right end of the stick yet...

Using the constructor is the right approach for that specific example that I gave.

To make an analogy, let's draw a parallel with StateT. Let's say I have a function of type:

f :: s -> m (r, s)

... and I want to embed that in StateT. The obvious way would be to just wrap it in the StateT constructor, but let's say that I didn't allow myself to use the constructor (or anything equivalent) and restricted myself to using get, lift, and put. Then I would have to write:

do s <- get (r, s) <- lift (f s) put s return r

Weird, right? That's basically how awkward it would be to use callCC for something where the ContT constructor is more appropriate.

Now, ContT differs from StateT in one important way: in StateT our mental model of state most closely matches the 'get' and 'put' commands, which is why we like to use those more frequently than wrapping things in the StateT constructors. However, with ContT the situation is the opposite. The ContT constructor most closely resembles our mental model for how continuations work, which is why we use it more often than we use callCC.

So I hope that explains why ContT is a bit unusual in that idiomatic usage tends to use the constructor more heavily than other monad transformers. It's just that internal type already closely matches our mental model so we don't need to deviate that far from the original constructor and abstract over it with higher-level primitives.

2/ For many monads (Reader, Writer, Error...), one can generalize any function "f1 :: xxxT m a" into "f2 (Monadxxx m) => m a" ; is it possible to do the same with MonadCont ?I guess it's not since MonadCont is a *single*-parameter typeclass, while ContT is a *multi*-parameter type constructor...Any insight about how it could or why it cannot be possible, would be much appreciated :) .

So, the answer to part 1 is that you would probably need to invoke two separate monad transformer layers (one for each type of continuation) then run each one separately (i.e. provide a separate continuation for each one). I'm not aware of a good way to combine two distinct return types into a single monad.

I don't know how to generalize `ContT` into a type class, but I do know how to generalize an equivalent monad into a type class. The idea is that you can actually implement `ContT` using the `Server` type from `pipes`, where you replace every continuation `k` with a call to `respond`. The argument to `respond` is the argument of the continuation and the return value of `respond` is the return value of the continuation. Then you feed the continuation using the `(//>)` operation from `pipes`, which is analogous to `(>>=)` for an indexed continuation monad.

In fact, you can actually show that this behaves exactly like an indexed `ListT` monad (where `(>>=)` = `for`/(//>)` and `return` = `yield`/`respond`). `pipes` provides a non-indexed `ListT` monad that you can study to see what I mean.

Now, `pipes` currently does not provide a type class for this, but it used to. If you study the `3.*` series of `pipes` you will see that there is a `Proxy` type class which is sort of like the `mtl`-ization of `pipes`. I ended up dropping it in version 4.0, but it's there for you to study.

So that's a long-winded way of saying that it is possible to type class this behavior if you implement it in terms of an indexed `ListT` monad and then type class that.

Hi Gabriel. I found this old article looking for literature about continuations.

Unlike in other functional languages, that lack a monad instance, there is a simpler way to see continuations in haskell:

the continuation is the 'f' in the bind operation. x >>= f

Because 'f' is the rest of the computation. It is not needed a structure above to deal with continuations in haskell, thanks to the monad instance.

I was very surprised whit this simple idea. It was in the process of avoiding callback hell. I tried to be as pragmatic as possible. So I created a simple monad in which I hold the 'f' in a state monad, so it can be executed at any time. In particular I created a client side framework with this, hplayground, that avoid the callback hell and has fully composable widgets. The whole idea is described here https://www.fpcomplete.com/user/agocorona/a-monad-for-reactive-programming-part-1. the drawbacks of this approach (that indeed would appear in any kind of continuation monad) are solved in a second part and other articles in the same site.