In response to my continuation elevator pitch, Daniel von Fange says Maybe I have not fully grokked continuations, but to me treating a "page" as a a method of an object makes more sense to me for 97% of things I do. Patrick Logan has an excellent reply:
Without getting into how continuations work, you can look at it this way: continuations allow a web page to behave just as you prefer: kind of like a method of an object.
What if methods in your favorite language required you to do the following: every time the method itself calls another method, the return value does not go back to the caller. Instead what if you had to name some other method to receive the return value?
In this case, as in typical web programming, you have to "name the continuation", that is, provide an explicit place for the computation to continue following the call (or the user interation, in the web scenario).
But in a continuation-based web server, just as in most programming languages, the system handles "where to go next" implicitly. That is, you do not need to provide an explicit "continuation". The system is happy to do that for you.
And so just as your method calls another method and the results are returned right there for you to use, your web page "calls the user" and the results are returned right there on the page for you to use.
This reminds me of a gedankenexperiment I've been toying with for a while. Let's say you had a programming language, maybe an odd dialect of BASIC, with only one IO primitive, called, say, Ptth (in memory of Bill the Cat?). Ptth has the following definition:
Ptth(outputString, functionName, extraArgs)
Ptth prints outputString onto the screen, and waits for user input. After the user presses enter, the function specified by functionName is called, with the user input as the first argument and the extraArgs array as the second argument. Ptth terminates the current execution thread, and so never returns.
What would programming with Ptth look like? Well, here's some pseudocode for a pointless program using PRINT and READ, adapted from a posting by Chris Double:
FUNCTION MAIN
PRINT "This program calculates Rate * Amount"
PRINT "What is the rate?"
$rate = READ
WHILE $rate <= 0
PRINT "Rate must be greater than 0"
$rate = READ
END
PRINT "What is the amount?"
$amount = READ
WHILE $amount <= 0
PRINT "Amount must be greater than 0"
$amount = READ
END
PRINT "The result is: ", $rate * $amount
END MAIN
Now let's try to write this using only Ptth (bear with me here):
FUNCTION MAIN
PTTH "What is the rate?", "GOT_RATE", []
END MAIN
FUNCTION GOT_RATE($rate, $extra)
IF $rate <= 0
PTTH "Rate must be greater than 0. What is the rate?", "GOT_RATE", []
ELSE
PTTH "What is the amount?", "GOT_AMOUNT", [$rate]
END
END GOT_RATE
FUNCTION GOT_AMOUNT($amount, $extra)
IF $amount <= 0
PTTH "Amount must be greater than 0. What is the amount?" "GOT_AMOUNT", $extra
ELSE
PTTH ("The result is: " + ($amount * $extra[0])), "EXIT", []
END
END GOT_AMOUNT
You're supposed to think that's considerably uglier than the version PRINT and READ, and well, it is, although the simplicity of the task makes it seem less ugly than it deserves to be. The point, of course, is that Ptth gives you exactly what Http does - the outputString is the HTTP response, the functionName is the URL embedded in, say, a form in the response, and extraArgs is whatever information you can cram into hidden fields and such. Continuations, on the other hand, give you IO that is analogous to the PRINT/READ combination.
The kicker, for me, comes when you try to refactor. It's pretty easy to pull the whole "loop checking for > 0" business out of the first version so that you end up with:
FUNCTION MAIN
PRINT "This program calculates Rate * Amount"
$rate = GET_GREATER_THAN_ZERO("rate")
$amount = GET_GREATER_THAN_ZERO("amount")
PRINT "The result is: ", $rate * $amount
END MAIN
How do you do the same simplification of the Ptth version? Submissions welcome.
If I have time, and people think it would be interesting, I'd like to take some of the dominant theories of structuring web control flow out there (the whole "model 2 servlet" thing comes to mind) and express them in terms of Ptth. Not only would this be interesting in itself, but it would hopefully bring home the point that people are solving the wrong problem. It's not that we need more complex abstractions over Ptth, it's that Ptth sucks (unless of course you have a powerful enough abstraction to transform Ptth into PRINT/READ).