There seems to be a miscomprehension that continuation
based and RESTful web apps are mutually exclusive.
Witness Nagare proudly proclaiming “no explicit URL routing / mapping …
no global session object … no REST” as if continuation
based frameworks were violently in opposition to these
features. This is not the case. Fundamentally the issue is
about managing state, and continuations, cookies, and
friends are all approaches to solving the problem of
encoding state over a stateless protocol. At Untyped we
develop web apps that use a combination of continuations,
RESTful URLs, and cookies for managing state and I believe
this is the correct way to approach the problem. I hope
this post will convince you of the merits of our approach.
Before looking at the tradeoffs of the different
approaches I want to summarise continuations and their use
in web applications. Simply put, the continuation of a
program is what happens next. In the program (+ 5 (+ 2 1))
the continuation of (+ 2 1)
is to evaluate(+ 5 [])
, where I’ve written []
to indicate the
place where the value of (+ 2 1)
goes. Now in Scheme we can
capture a continuation, store it in a variable, and
generally pass it around like any other value. This means
we can effectively suspend a computation (by capturing a
continuation) and then resume it at some time in the
future (by invoking the continuation, which in Scheme
appears as any a function application).
Now let’s look at what continuations do for web
applications. A continuation-based framework associates a
specific server state with a URL, which it does by
capturing a continuation when a response is sent to a
user. Everytime the user visits that URL they visit the
same server state, invoking the captured continuation. As
the user navigates around the site they build a history of
server states that can be revisited using the back and
forward buttons. This has several advantages. Firstly, if
you don’t use mutation the back button will just work,
because the user is just back to the same program state.
Pretty neat. Furthermore, continuations give you procedure
call semantics in your web app. Because a continuation is
resumed when a URL is visited, to your program it appears
as if the user’s request is the returned value of the
function that sends your response. It’s as if you were
using display
and read
on the
web. This makes programming a lot simpler. For example, if
you want to forward the user to a login page you just call
the login page function, and it will return to the right
place. No need to pass that page a URL to redirect the
user to. This can be incredibly productive.
Now we’ve seen some of the advantages of continuations, we
must consider the cases where the model falls down. There
are two main issues: server load, and scope. Server load
is simple. Every time you store a continuation on the
server you use up some memory (RAM or disk space). At some
point you have to reclaim that resource, so people may see
“continuation expired” pages if they leave a long time
between visits (though this is no worse that session
expiry, which is quite common). Often a website has pages
that are just displaying the results of simple queries to
a database. These pages have no interesting state and
using continuations in this case is wasteful of resources.
Here RESTful approaches are appropriate, and we use them
with, for example, the web server’s dispatchers.
Scope is another issue with continuation-based apps.
Recall that continuation-based frameworks associate a
particular URL, meaning a particular browser window (or
tab), with a particular server state. There are some kinds
of state that should be shared across all browser windows.
Login information is a prevalent example. If I login to a
site via one browser window, and then visit that site in
another browser window I expect to already be logged in.
This isn’t possible with continuations, as they are per
window. Cookies, on the other hand, are per browser. So
storing my login status in a cookie is the right thing to
do.
In summary, RESTful approaches (URL routing, for example),
cookies, and continuations are complementary and all have
a place in web applications. Don’t think, for example,
that is you use continuations you automatically reject
everything RESTful! Finally, the Anton of Straaten
addressed this issue from a different direction in his LL4 talk. Check it out for a different take on the problem.
Equivalently we could say the
continuation of (+ 2 1)
is (lambda (x) (+ 5 x))
. This realisation is the key to continuation passing
style, a program transformation useful in compilers and,
perhaps surprisingly, AJAX web applications.