« Hazards of continuation-passing | Main | Representing continuations »

Constructs for Composable Web Page Features

I've been thinking about how to get an appealing syntax for using continuations in the web environment.

Here is my guiding priniciple:

Page features should be composable: I should be able to take a feature that you've designed, along with your feature's control threads, and embed it in my page, even though my page has other features with other control threads.

This accords well with real web practice; for example it is common place to see an app having multiple screens, where each screen is designed independently, but a certain module is used on each one—for example, a search box which is available across the app. So, the goal is not merely to allow multiple buttons on one page but also to allow features to be designed separately and brought together in a modular fashion.

I played around with a bunch of different constructs that would help with this. This is my favorite so far. The handle body with handler construct binds the name "handler" within the body to a continuation that represents the handler. The result of the expression is just the value of body.

fun search_feature() {
    handle
       <form action="{handler}">
       <input name="search_term" />
       <input type="submit" value="Go" />
       </form>
    with
      {search_term=term} -> do_search(term);
}

I'm treating the handler portion as a pattern matching expression which expects as argument a record; the fields of the record are just the form fields. My reason for wanting to use pattern matching is that we might then embed multiple submit buttons in there, which could be recognized as different cases in the pattern match. I'm not sure the details of how that pattern matching works out.

But the handle construct itself should be relatively easy to implement. I've got continuations as first-class objects in Links with the syntax escape e in expr. Excepting the pattern matching, we should be able to implement handle as sugar for escape.

In the above example, the do_search() function ought to be one that never returns. Why? Because if it did return, the natural continuation for it would return to the caller of search_feature, which is not the desired behavior. The caller of search_feature is out of the picture once this particular handler is invoked. This implies two things: one, we must *force* do_search to be a non-returning invocation (perhaps using the type system) and two, we must not wrap up the surrounding continuation as part of the one called handler. That would be a needless waste.

Post a comment