Writing dynamic webpages with continuations can save a lot of pain, by letting the computer compile the inversion of control away. Unfortunately, continuations can rarely be exported outside the language implementation's runtime, so we are usually forced to save them at the server, and only pass a handle to the client. Not only may this open the server up to DOS attacks, but it also usually means that URLs have a finite, and relatively short, lifetime.
Common Cold (CC)'s continuations are serialisable,
avoiding these problems and much of the complexity associated with
them. Moreover, instead of attempting to 'hide' the fact that we do
not use native continuations, I tried to make the transformation as
explicit as possible (by having the package use its own special forms
instead of a codewalker), but still usable (by using its
own
CC supports both lexical bindings (static scoping) and special variables (dynamic scoping). Serialised closures or continuations always create fresh copies of the lexical scopes in the state they were when the closure or the continuation frame was created. Assigning to lexically bound variables can result in surprising behaviour, especially since closures/continuations that have not been deserialised have normal CL lexical bindings. Again, in the interest of explicitness, nothing is done to prevent this from happening; the programmer may very well know that a given variable is only assigned to before capturing its scope. Note that side-effecting lexically bound variables in continuations-based web framework is often a cause of weird bugs, since it can be difficult to know when bindings must be shared, and when they must be copied. Special variables are the prime way to introduce mutable bindings in CC. Specials created in CL code or via CC's own special forms can be mixed transparently, but only CC's special forms will capture them in continuations. Dynamic bindings are never shared between continuations, and are recaptured every time a continuation is captured. Assignments to special variables are thus carried from a continuation's invocation to the next.
Specials are also treated specially by the web
serving code. While the bindings are always saved in the
continuations, the topmost (non shadowed) ones are also exposed as CGI
parameters (as their qualified, URL-encoded, SYMBOL-NAME)
to the continuation's URL (this part may be buggy
with GET forms?). When a continuation is invoked (a
response is sent to the server), the topmost special bindings are
replaced (as if by assignment) by the rightmost parameter with the
corresponding name, if any. Parameters can thus simply be special
variables (I should create a special package for them to avoid their
being qualified), which will be side-effected with their value in the
form if any. Since CC uses Hunchentoot, it is of course also possible
to use Hunchentoot's functions for better access to parameters or
control of the headers.
Lexical bindings may be created
via bind, mlet* or mlet (if
the computed values may capture the continuations;
otherwise, let and let* are fine). Local
functions should be bound normally, using flet
and labels. Special variables may be introduced
with dbind, mdlet* or mdlet
(if no continuation will be captured in their dynamic scope, normal
CL special forms will also work). Finally, mprogn
introduces sequencing (when one of the forms may capture its
continuation), mcatch a catch frame (that
interacts with CL's catch and throw
correctly, but is captured in continuations),
and mblock, mreturn-from
and mreturn (which may not always interact correctly
with CL's block/return-from).
Page creating functions simply return a string,
and may use Hunchentoot's functions freely. To use
continuations, call-with-continuation-url
or
send/suspend may be
used. (call-with-continuation-url
[argument-function]) unwinds the stack (thus making it
closer to shift), builds a copy of the continuation's dynamic scope
environment, and calls its argument function, passing it the URL
representing its continuation. The argument function should return a
string, the web page's contents. (send/suspend ([k])
[body]) builds on top
of call-with-continuation-url and
executes [body] with [k] bound to the URL
representing send/suspend's continuation. The body
should evaluate to the response page's contents. In both cases, only
a copy of the dynamic bindings is restored. Side effects, even to
special variables, will not be reflected in the captured
continuations, and mcatchs, mblocks,
etc. are not active.
The continuation part of URLs are encrypted using AES
when a secret key has been registered
using register-key. Registering a zero-length key
disables the encryption (which is disabled by default). Continuations
are then base64 (adapted to URIs) encodings of unencrypted gziped
strings.
ensure-all-builders may be called at any time to
compile all fragment (closures or continuation frames) deserialising
functions. In would then be possible to export the data to other nodes
for easier load balancing.
Finally, make-continuation-handler should be
used to create a dispatch function for Hunchentoot. The example should
make its usage clear.
The user should mostly be interested in slambda and
sfunction, special forms that are, respectively,
like lambda and function, but return
closures that can be serialised (printed readably).
Exports symbols related to dynamic-binding and catch frames, and
the macros for the continuations mini
language. invoke-cont may be interesting to someone
building something else.
Fills the COMMON-COLD package, which is the only one that should
be USEd by the user. Defines ensure-all-builders, all
the web interaction-related functions/macros, and all the
continuation encoding machinery.