Saturday, July 31, 2004

Continuations In Mozilla

I came upon two interesting subjects recently:
In the spirit of Roman Porotinkov's AOP posting, I'd like to add this attempt to explain continuations with a short implementation in JavaScript. Here's are three sample functions that we'll work with...

function capitalize(str) {
// #a - next line to be explained soon.
capitalizeReturns[capitalizeReturns.length] = __cont__;
return str.charAt(0).toUpperCase() + str.substring(1);

function prettify(firstName, lastName) {
// #b - next line to be explained soon.
prettifyReturn = __cont__;
firstName = capitalize(firstName);
lastName = capitalize(lastName);
return firstName + " " + lastName;

function formalName(personName, prefix) {
var parts = personName.split(',');
personName = prettify(parts[1], parts[0]);
return prefix + " " + personName

// #c - the following vars will be explained soon
var __cont__ = null;
var prettifyReturn = null;
var capitalizeReturns = [];

Here are some sample invocations...

formalName("yen,steve", "Mr."); // returns "Mr. Steve Yen"
formalName("bush,george", "President"); // returns "President George Bush"

Up to this point, the __cont__ value has been null. Let's make __cont__ useful by introducing some continuation conversion functions...

function continuate(funcName) {
var funcText = uneval(this[funcName]);
if (funcText.charAt(0) == '(')
funcText = funcText.slice(1, -1);
funcText = funcText.replace(/return\s*(.*?);/g, "return __cont__($1);");
while (true) {
var newStr = funcText.replace(/(var\s+)?(\w+)\s*=\s*(\w+)\s*\((.*?)\);/,
"return $3($4, function($2) {");
if (newStr == funcText) break;
funcText = newStr + ")}";
funcText = funcText.replace(/\{/, "{ var __cont__ = getContinuation(arguments); ");
return this[funcName] = eval("(" + funcText + ")");

function getContinuation(args) {
var cont = args[args.length - 1];
if (cont != null && typeof cont == "function")
return cont;
return function(res) { return res; }; // An identity function.

That's it! Next, we'll convert our sample functions by "continuate"-ing them...


Now, let's go get some continuations...

var prettifyReturn = null; // holds the continuation to prettify()
var capitalizeReturns = []; // holds the continuations for capitalize()

formalName("bush,george", "President"); // returns "President George Bush"
prettifyReturn("Bill Clinton"); // returns "President Bill Clinton"
capitalizeReturns[0]("George H"); // returns "President George H Bush"
capitalizeReturns[1]("Washington"); // returns "President George Washington"

How does it work? The calls to continuate() transformed the original three sample functions to look like...

function capitalize(name) {
var __cont__ = getContinuation(arguments);
capitalizeReturns[capitalizeReturns.length] = __cont__; // same line #a as above
return __cont__(name.charAt(0).toUpperCase() + name.substring(1));

function prettify(firstName, lastName) {
var __cont__ = getContinuation(arguments);
prettifyReturn = __cont__; // same line #b as above
return capitalize(firstname, function(firstName) {
return capitalize(lastName, function(lastName) {
return __cont(firstname + " " + lastName);

function formalName(personName, prefix) {
var __cont__ = getContinuation(arguments);
var parts = personName.split(",");
return prettify(parts[1], parts[0], function(personName) {
return __cont__(prefix + " " + personName);

This is just a poor-man's continuation-passing style transformation. The function invocations of capitalize() and prettify() have been converted to take an extra closure parameter -- the continuation. Of course, JavaScript supports first-class lexical closures. Way to go Brendan.

The getContinuation() function works because JavaScript supports variable number of function call arguments (varargs). So, we can also tack on a continuation closure as the last argument to our function calls and retrieve it using arguments[arguments.length - 1].

At line #b in the sample functions, the variable prettifyReturns is assigned the continuation closure for the prettify() invocation. At line #a, since capitalize() is invoked twice, first on "george", second on "bush", we use an array (capitalizeReturns) that records those 2 continuation closures.

About continuate():

We don't have Lisp-style macros in JavaScript, which would have made this more powerful (but harder to understand?). But, modern versions of JavaScript (JavaScript 1.5, in Mozilla Firebird) support uneval(). With that, we can apply the popular bag-o-regular-expressions to do our function text munging. The regexps I put into continuate() really only scratch the surface of possible transformations. So far, they only convert the following function call styles: var x = foo(args...) and x = foo(args). I'm sure if you had a real JavaScript parser (maybe even one written in JavaScript), you could try a full continuation transformation approach that Paul Graham sketches out in On Lisp. But, none of us have that much free time.

Anton van Straaten on Lambda-the-Ultimate says that "a continuation *is* a closure". And, we've just walked through continuations implemented as closures. A lot of closures. Just see how unpretty the prettify() code became, and while easy to understand, it'll be even slower to run.

Finally, with my understanding to continuations and how much state and resources they can chew up, I don't see how continuation-based web programming (like Seaside) can scale up.

Other caveats:
  • I developed this using standard Rhino, the JavaScript 1.5 implementation in Java.
  • It only works in JavaScript 1.5, hence this will (likely) work in Mozilla Firebird and (definitely) not in IE, because of uneval().
  • By using varargs in this toy implementation, it means that your functions can't also use varargs cleanly.

Running With JavaScript Scissors

Beginners can easily hurt themselves when wielding any promising but unfamiliar technologies. Sometimes our early technological bets turn out to be bad choices, but we don't find out until too late (at least too late to restart a project).

From my Google searches, Simon Willison and Peter-Paul Koch seem to have the leading edge observations on how to safely wield DHTML and JavaScript. They show us how to move beyond old-style DHTML+JavaScript (to which most of us probably crossed our eyes at). Their proscriptions on how to use DHTML+JavaScript correctly, thankfully, do not require new languages or browser capabilities.

Peter-Paul Koch:
Why do we need a new language/specification for Web applications? JavaScript is inherently superior to any new specification because it's already there.

Peter-Paul Koch again:
JavaScript has a PR problem. ... needs a new job description. It's going to have to prove itself all over again to a critical audience of web developers who want to know exactly why they should use JavaScript. ... JavaScript is meant for adding layers of usability (and not presentation) on top of structural, simple XHTML documents. These layers of usability will probably allow users to organize the data they receive, and maybe also to send out new data queries.

Although there are countless applications and programs that do this, JavaScript is unique in being seamlessly integrated in 89% of the most important data access applications there are: the browsers.

Their guidelines for modern, leading-edge "doing DHTML/JavaScript right" boils down to, no surprise: separation of concerns. We must separate structural markup (HTML) from presentation logic (CSS and JavaScript). Or, as Stuart Langridge calls it, the new successful pattern will be Unobtrusive DHTML.

UPDATE: Adding Aaron Boodman to the pantheon of thinkers here.

Wednesday, July 28, 2004

Rich Browser Apps and Language Bigotry

Folks are talking about browser-based rich clients again and making interesting predictions on the future of application GUI's. I had long ago written off technologies like DHTML and JavaScript as just ugly UI hacks meant for silly animation and annoying popups, but it's now time for another look.

Especially with a second look at JavaScript, I'm very pleasantly surprised.

Yes, that's coming from someone who had written off JavaScript as a just a toy.

Let me catalog my inbred language bigotries. I'm an old Lisp hack -- learned it way back in high school. Then delved into C and Tcl. Then C++ (argh, including early Component Object Model, early CORBA) and Java. Most recently, I've used Python/Jython. And, I have sworn off XSL - terrible write-once stuff. I have fond memories of the very high productivity of the Lisp days with its function-oriented interpreted model. Long live the late-bound read-eval-print loop for productivity.

On JavaScript, I had no idea it had the following interesting features:

For Lisp folk:
  • first class anonymous functions
  • lexical closures
  • eval and uneval
For folks who can't stand Lisp syntax:

  • a loosely-typed Java-like syntax (ok, this I knew already)
  • simple list syntax, like ["hello", "world", "in", 2004]
  • simple associative array syntax, like { republican: "Bush", democrat: "Kerry" }
  • objects are associative arrays, like { firstname: "Conan", lastname: "The Republican" }
  • variable length arguments (varargs)
  • built-in regular expression support
  • private, protected, and public methods and fields
  • prototype based language features. I did not understand prototypes until, oh, last month, and I am starting to believe they are more flexible and powerful than C++/Java-style OO.
In short, JavaScript is a dialect of Lisp (Scheme?) with a Java-like syntax that most folks can live with. And, it has lots of surprisingly powerful "duck typing" features.

Unlike Jython/Python and Ruby (which are also very interesting), JavaScript is already deployed to >90% of the world through the web browser.

Hmmm, I wonder how far you can really go with JavaScript....

Saturday, July 24, 2004

Inevitable Surprises

My bedtime reading right now is Inevitable Surprises by Peter Schwartz. While ripping through it at the bookstore, I caught his demographic conclusions about millions of spouse-deprived Chinese men who will soon be randying about the Asian sphere -- in search of wives (and work). That's just one inevitable future consquence of government policies - the Chinese government's infamous one-family-one-child law. So, on that fun gem, I plunked down my $16 to bring this book home.

Lucikly, I won't be one of these men -- I'm happily married.

Another surprise is Schwartz's assertion that the population bomb is done -- the growth curves are flattening out. Quite a surprise to me. I wonder what he'll say about Moore's Laws curves (inevitably?) flattening out.

So far, Inevitable Suprises has been a good quick read that correlates with my other recent readings (Tom Peters Imagine! and Ray Kurzweil The Age of Spritual Machines). That's a pretty futurist-heavy input lately.

That's so 2004

I've surrendered to hype. Trying my first weblog. It'll be about my favorite tech, startup, and self-improvement musings. Let's see how it goes...