leastfixedpoint

A Rhino at the Seaside?

This page is a mirrored copy of an article originally posted on the (now sadly defunct) LShift blog; see the archive index here.

Tue, 18 July 2006

A couple of weeks ago I picked up Chris Double’s server-side javascript implementation, which uses the Mozilla Project’s Rhino Javascript environment with Jetty to provide a Javascript-controlled Java Servlet webserver.

The code’s available both for browsing and for darcs download:

darcs get http://www.lshift.net/~tonyg/javascript-server/

After adding support for Jetty’s SessionHandler class to Chris’s example.js, I downloaded the prototype.js Javascript utility library and got it running in a server-side environment1. The next step was using Rhino’s continuation support to implement the equivalent of PLT Scheme’s send/suspend/dispatch (also seen in Seaside, under-the-covers as part of the HTML-rendering and workflow aspects of the system, and in SISCWeb, which is at the core of our Icing library).

Here’s a little workflow, roughly equivalent to Seaside’s Counter application:

sv.addEntryPoint
("/count", // [1]
 function (servlet, bindings) {
     var finalC = servlet.withState
     (10, // [2]
      function (c) { // [3]
          while ( // [4]
                 servlet.sendAndDispatch
                 (function (embedUrl) { // [5]
                      servlet.replyHtml
                      (doc(”Counter”,
                           <>
                             <p>{c.value}</p>
                             <p>
                               <a href={embedUrl(function(){
                                        c.value++; return true})}
                                >More</a>;
                               <a href={embedUrl(function(){
                                        c.value–; return true})}
                                >Less</a>;
                               <a href={embedUrl(function(){
                                        return false})}
                                >Stop</a>;
                             </p>
                           </>));
                  }))
          {
            // Nothing to do in the body of the loop.
          }
          return c.value; // [6]
      });
     servlet.replyHtml(doc(”Bye!”, <p>Bye! {finalC}</p>));
     // [7]
 });

Points of interest:

Javascript is a little like Scheme - but not enough like Scheme to avoid the pitfalls of using ordinary local variables in a web workflow. The problem is that there are two ways you might want local variables to behave as the user back-and-forwards around your workflow, both perfectly reasonable and appropriate at different times:

The first option seems to me more functional in style, and the second more object-oriented.

To produce the second, object-oriented effect using these Javascript servlets, simply declare variables as Javascript locals and assign to them. The first is trickier: all variables are mutable, and there’s no pleasant syntax for functional-style rebinding of variables, so I’ve resorted to the withState method seen in the example above.

The basic idea is that we should reify functional variables (since they’re the exception rather than the rule in Javascript; in Scheme, we’d probably reify the mutable ones!) and use a system very much like Scheme’s dynamic-wind to make sure the correct values are visible at each stage in the workflow. Here’s a more focussed example of withState usage:

var finalResult =
  servlet.withState(initialValue,
                    function (stateCell) {
                      // … code using stateCell …
                      return finalValue;
                    });

The initialValue gets placed into a fresh managed cell, which is bound to stateCell for the duration of the function. The code in the function should access and modify stateCell.value, and the values will be tracked automatically across the forward and back buttons. The final result of the function is used as the final result of the whole withState call. Once withState returns, stateCell is no longer automatically tracked - it has gone out of scope, in a way.


Footnote 1: Tricks were required to get prototype.js running - but they were as simple as defining document = {}; window = {}; navigator = {};.

Comments

On 19 July, 2006 at 4:44 am, Chris Double wrote:

Very nice! Thanks for making it available.