Chapter 4. Managing Program State

Table of Contents

Introduction to Statefulness
State in Traditional Web Applications
Continuations and State
Finer Statefulness
State with SRFI-39 Parameter Objects
State with Web Cells
State with the Session Object

Introductions to web programming often mention the statelessness of the HTTP protocol, to then describe how cookies and sessions solve the problem. This is not a complete solution however: being "sessionful" is not the same as being "stateful."

Cookies and sessions are mechanisms laid on top of the HTTP protocol respectively to: a) allow an application to recognize two requests coming from the same browser as such, and b) to store data common to such requests.

Neither cookies nor sessions completely address the issue of statelesness because, while programs have the means to associate two requests to a given user (as they contain the same cookie), they have no means by which to associate two requests as being part of the same navigation flow. It is in fact possible to manually place requests out of sync with program state, and often trigger incorrect behavior. Traditional web applications are thus less like a program with a GUI interface and more like an API open to the world. Forced browsing, or even using the back button, can cause an application to misbehave in ways that are not easily foreseeable as application complexity increases.

Aside from state, the Session object is common to all requests from the same browser, so simply using a web application from two windows can produce concurrency problems such as loss of consistency or race conditions. Moreover, since the session is a global scope for the application, in fact the only scope that traditionally persists from request to request, developers are unable to take advantage of the finer scoping mechanisms available in modern programming languages.

One last major issue with traditional web applications is that they are made of short-lived code snippets which, after parsing the request in the context of the session, generate a response and exit. Because their state is lost, it is impossible to use structured programming techniques in code spanning multiple requests. Even the simplest for-loop must be expressed in terms of a (global) session variable increment and a GOTO (HREF).

Requires: (import srfi-39)

A complete treatment of parameters is available in SRFI-39. This section is concerned with the use of the dynamic environment to maintain state shared by all requests for the same user in the same execution flow.

SRFI-39 parameters are essentially dynamically-scoped variables. They can be defined in the global or the module scope and then bound to the dynamic scope -- the scope of the execution flow -- in the application entry point through the parameterize form.

When using send-*/[suspend|forward], the parameters, being part of the dynamic environment, are captured in the suspended state -- the suspension of the program execution is transparent to parameter bindings.

However procedures stored with @href-p-style attributes or the forward/store! procedure will run in the base dynamic environment, and thus will see fresh values of the parameters. In those cases on can use forward/dynenv/store!, which instead preserves the dynamic environment.

The Counter with SRFI-39 Parameters example demonstrates this technique.

Requires: (import siscweb/webcells)

Web Cells are a way to track program state according to the navigation flow. They are described in full in the paper Interaction-Safe State for the Web.

In essence, cells establish a dynamic scope over the navigation path of a user. Successive pages can overshadow values of bindings set in previous pages, but do not destroy them.

If a user backtracks the browser window, previous values are again visible. If the user clones the window and proceeds through two different navigation branches, each branch sees the values it overshadows.

The Counter with Webcells example demonstrates this technique.

Requires: (import siscweb/session)
Located in: siscweb.jar

In some situations a global, per-user scope may be desirable. This approach is more prone to concurrency problems, but its semantics is very straightforward.

The Session object can be accessed via a syntax similar to that of SRFI-39 parameters, or via Scheme wrappers around the Session.get/setAttribute() calls. While the former is appropriate for storing Scheme values, the latter is provided as a means to store and retrieve Java objects (or java-wrapped Scheme values.)