Skip to content

OpenLaszlo software development position open

Laszlo Systems has a position open for a senior software architect to work on the OpenLaszlo client runtime. This is a paid position, and you would be working on an open source project. You’ll be working on the bright green box below, but if you qualify, you get to choose your color. :-)

You should be a great software designer, able to navigate complex systems and big architectures, but with wizard-level performance optimization skillz too.

Your idea of a dream job should be writing code that does near-impossible things, and shipping it, and then seeing it used to build products that you can show your friends. But you should also enjoy working with other smart people, bouncing ideas off of them, learning from them, and teaching them. In fact, you should be willing to mentor and lead the efforts of open source contributors, and you should have the leadership and communication skills to do so.

You should enjoy initiative, innovation, and excitement. You’ll be working on the core technology of an emerging technology company. This is an important, visible position, that will change the future of the web.

You should enjoy building complicated things, and making them look simple. You should be excited about working on systems that look like this on the inside:
but look as simple as this to developers:

<canvas>
  <button onclick="animate('x', 100, 1000)">Click me!</button>
</canvas>

and can be used to build things that look like this to your mom:

You don’t need to know Smalltalk, Lisp, Dylan, JavaScript, Python, or Ruby — and you won’t be using any of them except JavaScript — but if you’ve tried dynamic languages before and hated them, you probably don’t want this job either. Conversely, if you’ve never profiled an application or made an educated performance-based decision about whether to use integers or floats, recursion or iteration, or DTO or parameter lists, you should probably look elsewhere too.

You should live in one of America’s Bay Areas (San Francisco or Boston).

You can read more about this position here and here. Send your resume to resumes@laszlosystems.com.

Wasting Time with Laszlo

Last month, Geert Bevin wrote an article called wasting time with Laszlo. Since then, he’s wasted even more time with Laszlo. Bla-bla List is Bevin’s fully functional clone of Ta-da lists, re-implemented in Java and RIFE.

Of course, while the bad thing about web application programming is that you’re dealing with two different platforms (even if one of them is just DHTML), the good thing is that you can choose them independently. I’m planning to use Laszlo on the client with PHP and Rails on the server, now that serverless deployment makes that possible.

Site_”Ta-da lists”:http://www.tadalist.com/_”Bla-bla list”:http://blablalist.com/
Server languageRubyJava
Server frameworkRailsRIFE
Client platformDHTMLLaszlo

Update

In OpenLaszlo 3.0b2, two weeks later, Bevin updates his original list of criticisms to take into account the 3.0b2 release. Recommended reading for anyone evaluating RIA platforms.

PyCon 2005

The slides from my PyCon 2005 talk about Jython and the OpenLaszlo compiler are online (PDF). I’ve corrected some dates and minor typos: 3K+1.5K does not equal 6K, as I realized once I was standing at a podium with a large slide that displayed my math skills behind me, and a large number of technically sophisticated smart people in front of me.

Some illustrations from the talk:

KRANK dataflow

Dataflow for implementation of the KRANK feature for startup time optimization:

Script compiler dataflow

Dataflow for the OpenLaszlo ECMAScript compiler:

(The PDF shows what the data looks like at some of these stages.)

Serverless deployment

Deployment diagrams with (top), and without (bottom), the OpenLaszlo server. Deploying with the server requires a J2EE servlet container (Tomcat, JBOSS, Weblogic, Websphere, etc.). Serverless deployment works with Apache and consumer-price ISPs (such as the one that hosts this website).

Deployment with OpenLaszlo Server

Serverless Deployment

(Caveat: the server also handles caching, GIF transcoding, RPC, and the persistent connection, as this animation illustrates. The diagram above just shows the initial download.)

Laszlo Mail

Last week Laszlo announced its web mail application. Later this year Earthlink will deploy a branded version of this application.

*Update: Laszlo Systems is hosting open registration for Laszlo Mail here.*

Laszlo Mail is being built on top of Release 3.0 of the OpenLaszlo platform.

Congratulations to everyone involved in writing this application, and to the people who have been worked on the platform extensions that make it possible to write applications like this in Laszlo. Writing good client applications is hard work.

Here’s where I plan to use the thing:

Right now, when I’m using someone else’s computer (still happens — the beasties don’t fit in my pocket yet), I have a choice between (1) setting up an IMAP client, or (2) using an HTML webmail interface. These days I always opt for the webmail client, and GMail actually makes it bearable.

But…

I use folders to organize my email. I use a variant of GTD, with folders for projects, context-sensitive items (tickles for future dates, agendas for future meetings), reference material, and archives. GMail just doesn’t play well with folders. Sure, I can make folders in GMail, but my GMail folders don’t respect any organization I’ve created in IMAP. And I’m a drag-and-drop guy — it’s just too hard to move messages between folders with GMail, which is why I don’t use it as my main mail client in the first place.

So right now, I’m stuck with a bifurcated mail experience. The desktop clients (Thunderbird on the PC and Linux, Apple Mail on the Mac) have the UI features I need; GMail (and before that SquirrelMail) have the ubiquity.

That’s what I’m looking to Laszlo Mail to fix. I want to be able to walk up to any computer in the world, catch up on my mail, easily move it to the right folders, and go back to my desktop machine and see messages where I put them.

Some of you do everything on your Blackberry or iPaq. You don’t need the thing. Some of you keep everything in one big Inbox. You can probably get by with GMail. For me, I’m just counting the days…

Ruby and Laszlo

I first heard of Ruby at the second Lightweight Languages Workshop 2, where Matz and I were both speakers. This was first public disclosure of the then-proprietary Laszlo platform language. I’m afraid I was more worried about preparing my talk then listening to Matz at the time!

Since then, a number of different people have expressed interest in both Laszlo and Ruby. I figured I had finally better take a look at it.

I understand what the fuss is about. Ruby is one of the rare languages with a readable embedded syntax for metaprogramming, it’s well designed, and it has a mature library for web programming. What this translates to in practice is that you can program in a language with “keywords” (really just functions) suitable to the task at hand.

Compare the two class definitions below:

class Person < Object
  attr_reader :name
  attr :location
end
class Person < ActiveRecord::Base
  has_one :name
  has_many :address
end

The first definition uses the core language syntax to define a Person class that contains a getter for name, and both getters and setters for location. The second uses Rails to define a Person Active Record that has one name record and many address records. (It uses the Foreign Key and Association Table patterns, respectively.) The cool thing is that attr and has_one look the same to the library user. Ruby allows the library developer to grow a language. This lets the library user write in a concise domain-specific language that embeds Ruby.

What does this mean in practice? During my last vacation it took about five lazy vacation days with Ruby on Rails to implement a fairly sophisticated 40-page web application with five models, two metamodels, CRUD, cookies, image upload, and login. (I’ll write more about the application itself, if I find a few free weekends to harden it for public use.) For comparison, it took me about the same amount of time during my previous vacation to write a much simpler ten-page PHP web application that had only one model. And I already knew a little bit of PHP, whereas I was learning Ruby and Rails from scratch.

Now, what does this have to do with Laszlo? Laszlo certainly doesn’t have any metaprogramming facilities. It has states, constraints, and data binding, which extend the reach of declarative programming beyond static layouts. (Yeah, yeah, I should write about this too; in the meantime there’s docs and examples here and here.) I suspect that some of the people who “get” how to use metaprogramming in Ruby also get how to do declarative programming with the Laszlo features. But I also think a large part of what Laszlo brings to the table is simply that it allows you to use conventional OO techniques in client-side browser programming. For example,

<canvas layout="axis: y">
  <view width="10" height="10" bgcolor="red">
    <view x="1" y="1" width="8" height="8"/>
  </view>
  <view width="20" height="20" bgcolor="red">
    <view x="1" y="1" width="18" height="18"/>
  </view>
  <view width="30" height="30" bgcolor="red">
    <view x="1" y="1" width="28" height="28"/>
  </view>
</canvas>

DRYs out to:

<canvas layout="axis: y">
  <class name="box" width="${this.size}" height="${this.size}" bgcolor="red">
    <attribute name="size" value="10"/>
    <view x="1" y="1" width="${parent.width-2}" height="${parent.height-2}"/>
  </view>
  <box/>
  <box size="20"/>
  <box size="30"/>
</canvas>

This (OOP) is old hat in the server-side world — just like MOP is old hat to Smalltalk and Common Lisp developers — but it’s relatively new in the world of zero-install client-side platforms. So I think the analogy between Ruby on the server, and Laszlo on the (much more resource-constrained) client, is that each of them advances advances the reach of non-academic programming:

Update: Now that I’ve done more Ruby and DHTML programming, I can see that the diagram above gives OpenLaszlo short shrift. Although OpenLaszlo is lower level that Ruby with respect to code generation and a MOP, the use of databinding and constraints makes it higher level in a different set of ways.

Laszlo’s Boston Office

Laszlo’s main office is in a spiffy office building in San Mateo.

But Boston Product Development (basically, the OpenLaszlo development team) works out of a cafe.

Espresso Royale Cafe

This puts us in good company. (And we get better coffee, too.)

Henry posted some photos he took last week. Here’s one; click on the image for more.

Oliver, Amy, Tucker, and Henry

Three Lefts Make a Right: The Type Declaration Paradox

A few days ago I argued that even though type declarations aren’t the best possible solution for any particular problem, they can be the right solution for solving several problems at once. I baffled even smart people. If I had longer I’d write a clarification. As it is, I’ll just give an example.

Type Declarations as Documentation

Let’s say that I’m writing a function f() that takes two arguments:

function gcd(a, b) {...}

I know as I’m writing the function what the range of parameter values is intended to be. They’re both non-negative integers. I could do at least four things to document this: add a comment, use descriptive variable names, add assertions, or insert type declarations.

To make this interesting, let’s assume this is in a language that doesn’t have a “non-negative integer” type and doesn’t have a mechanism to create restrictive types [1].

Comment encoding:

// a and b are non-negative integers
function gcd(a, b) {...}

Structured comment:

// @param a : Integer
// @param b : Integer
function gcd(a, b) {...}

Variable name encodings:

// Smalltalk style (loses information):
function gcd(anInteger, anotherInteger) {...}
// Extended Smalltalk:
function gcd(aNonNegativeInteger, anotherNonNegativeInteger) {...}
// Hungarian notation:
function gcd(nA, nB) {...}

Assertions:

function gcd(a, b) {
  assert a isinstance Integer && a >= 0
  assert b isinstance Integer && b >= 0
  ...
}

Type declarations:

function gcd(a: Integer, b: Integer) {...}

Note that the type declaration, from the perspective of how much information it captures, is the worst of these: it loses the information that a@ and @b are non-negative. (It’s tied with the short form of the Smalltalk convention here.)

This is because the type declaration is only being used for one purpose: documentation. And comments are better at documentation.

Let’s summarize the benefits of these solutions numerically. I’m pulling the numbers from a hat (with some attempt to make the rank match my perception); what’s important is that, according to this metric, type declarations aren’t the best solution for documentation. More verbose mechanisms, and mechanisms that violate DRY, get docked in the cost column.

mechanismcostbenefit
comment14
name32
assertion24
type declaration13

Type Declarations as Metadata

Now add another requirement: this function should be callable from a statically typed language, e.g. C. (You can substitute your own requirement: compile-time checking, database bridging, etc.) There are a couple of ways to do this too:

Metadata:

@cexport(bool gcd(int, int))
function gcd(a, b) {...}

Type declarations:

function gcd(a: Integer, b: Integer) : Boolean {...}

Again, the type declaration is more concise, but it’s not as general a solution to the problem of exposing a function in one language to code in another. The metadata solution allows me to choose an export name for the function, and perhaps do a better job of selecting equivalent types than a library or tool driven solely by the types in this language might perform. It does so at a significantly greater cost: in verbosity, and in the duplication of structure and names which now have to be maintained in parallel. Numerically:

mechanismcostbenefit
metadata24
type declaration12

Mow, for any particular use of metadata, the cost-benefit ratio may be different. For example, a tool driven by the metadata may not do any better a job than one driven by the type declarations, in which case the cost-benefit ratio may look more like this:

mechanismcostbenefit
metadata22
type declaration12

Adding Apples and Oranges

So far type declarations are 0-for-2. They’re worse than comments at documentation, and they’re worse than domain-specific metadata at bridging languages. But type declarations are stronger when they take on both challengers at once.

Let’s consider a function definition that satisfies both documentation and metadata requirements. I’ll consider two solutions: (1) a “swat team” solution composed of the best documentation solution (comments) together with the best metadata solution (a domain-specific annotation); and (2) type declarations:

Swat team:

// a and b are non-negative integers
@cexport(bool myGCD(int, int))
function gcd(a, b) {...}

Type declarations:

function gcd(a: Integer, b: Integer) : Boolean {...}

Numerically:

mechanismcostdocumentation benefittooling benefit
comment14
metadata22
comment+metadata342
type declaration132

Again, don’t pay two much attention to the actual numbers; the point to note is that even though a comment is better at documentation, and metadata is better (or in this case as good) at tooling, the comment+benefit solution has a worse cost-benefit ratio (3:6=1:2) than the type declaration solution (1:5). This is because the cost for type declarations stays constant while, the more requirements type declarations meet (even if poorly), the more the benefits rise. This as as opposed to the swat team approach, where the cost rises with the number of requirements (and therefore solutions).

Setting aside the math, it’s obvious at a glance that the swat team example is longer, and more complex, and therefore should be expected to have more overhead. A simple change, such as adding a parameter to a function, becomes a lot more expensive, because you’ve got to make each change so many places.

Notes

1 In fact, this language could even be Java. Consider a gcd that worked on BigDecimals. There isn’t a “non-negative BigDecimal” type, so a type declaration can’t encode the domain of the function.

The Type Declaration Compromise

A vice grip is the wrong tool for every job. — anonymous

Type declarations aren’t the best tool for any particular purpose, but they’re a passable tool for a lot of different purposes, and therefore they’re often the best tool for meeting several purposes at once. There are better ways to comment a program, to add metadata for tools and libraries, or to verify program correctness; but type declarations, in many languages, are a passable way to do all of these jobs at once.

The situation is similar to that of a convergence device such as a camera phone. The voice quality on my camera phone isn’t that great (the speaker shares a tiny clamshell lid with the camera optics and CCD), and the camera doesn’t take great pictures (the lens is smaller than a halfpenny), but I’d rather carry one substandard device than two separate best-of-breeds — even if I have to go get my “real” camera when it’s worth taking a good photograph. You can make the same argument for a PDA phone versus a laptop. And I believe the same holds for type declarations, versus all the mechanisms that, considered separately, are superior to them.

This is why my ideal language has optional type declarations (unlike Java, in which they’re mandatory, and Python, in which they’re absent). I’d like to have the syntax available, for when I want to use a compromise that meets several of these requirements at once, but optional, so I can instead use features that are better tailored for each individual requirement, without redundancy.

Some of the uses for type declarations are:

  • Documentation. The type of a variable is more informative than just its name. For example, f(a: String, b: Integer): Boolean is a better starting point for understanding the intent of f@ than @f(a, b) alone.
  • Code Generation. An IDE can use the declared types to drive context-sensitive help such as docstring tooltips (“intellisense”). Foreign function generators such as jnih and SWIG can use the type declarations to create function interfaces that stub or bridge to other languages or libraries.
  • Runtime Introspection. Foreign function libraries can introspect variable types and function declarations to make foreign function calls at runtime. Database bridges can introspect class definitions to generate CREATE TABLE statements, and to bridge RDBM tables and instance hierarchies.
  • Compiler Optimization. A compiler can use type declarations to choose efficient unboxed representations, or (in a dynamic language) to elide runtime type checks.
  • Program verification. Type mismatches are detected closer to the source location of the error (when a String is passed to a function that expects a Float), instead of further down the call chain (at the point where a primitive operation such as @/@ is applied to a String). With compile-time type checking, type mismatches are detected even in the absence of a test case or code coverage.

There are better solutions for each of these requirements, when the requirement is taken on its own:

  • Documentation: Comments are generally superior to type declarations, because natural languages are more expressive and extensible than type systems. A simple type system can’t say very much, and a complex type system can be hard to read.
    For example, I want to know (and declare) that table is a Set of String, not just that it’s a Set. If my language (say, Java 1.4) can’t express this, I’m going to write table: Set<String> in a comment anyway. In this case, why write table: Set as well?
    Another example holds in languages without type synonyms (such as C’s typedef), such as Java up through Java 5. I generally want to know an object’s abstract type (Centimeters), not its concrete type (float or double).
  • Code Generation and Runtime Introspection: An extensible metadata system is a more general solution, and will work in cases where type declarations fall short. Again, the type system for one language doesn’t generally describe the information that is necessary to convert types into another language. Most languages have type systems that can’t distinguish between required and optional values (a String, versus either a String or null). This means, for instance, that you can’t infer a RDBM type from a declared type, because you don’t know if the value is nullable.
  • Compiler optimization: Type inference and optional type declarations are often better solutions than mandatory explicit type declarations, where this feature is needed at all. The weak point in many programs these days is the feature set, quality, or development time, not the execution space and speed. Even when optimization matters, explicit type declarations are often both too much and too little:
    Too much, because they often aren’t necessary: even a C++ or Java compiler infers the types of expressions, and it’s some arbitrary to turn this inference off at the function level. Sure, type declarations are useful at compilation unit boundaries to avoid whole-program analysis, but declaring the type of every variable and private function is a throwback to the 90s 80s 70s 60s.
    Too little, because program analysis is necessary anyway in order to infer much of what an optimizing compiler or modern memory manager needs to know: flow, lifetime, extent, aliasing, mutability.
  • Program verification: Unit tests, assertions, and contracts are better suited for this purpose alone. I rarely see code without a test case that actually works; the clean compile (even with type declarations) just means the easy part is done. Usually even the value-related errors are division by zero or invalid use of null, not something the type system would have caught; and the types of bugs that I see once I get to a clean compile are similar in Java (with mandatory type declarations) and Python (without type declarations at all).

Given the inadequacies of type declarations for each of their intended purposes, then, why use them at all?

Compromise

One reason is that a type declaration doesn’t just a poor job of one of these things; it does a poor job of each of them, and that means it does a good job of doing them all at once. If adding “: Integer” to a program produces an incremental improvement in both documentation and IDE support, and SQL and foreign function bindings that work some of the time, well, maybe that’s worth it, even if the documentation value alone wouldn’t have been justified doing this in addition to (or instead of) writing a comment.

I argued above that it wasn’t worth writing table: Set, since Set doesn’t say much of what I know about the value of table — but if writing “table: Set” also enables a compiler optimization and some early error detection, then maybe I’ll write table: Set instead of /* table: Set<String> */, in order to reduce the (partial) duplication in my source code that would be present if I wrote the ideal comment and the ideal (within the constraints of the type system) type declaration separately. Eliminating the comment in this case is arguably an application of DRY.

Removing the extra information in the comment hurts the quality of my documentation, by using a lowest-common-denominator (the type system) instead of a tool (natural language) that is tailored for the job. The fact that the type system is a compromise among several uses means that my use of it is a compromise too.

Concision

There are two other advantages of using type declarations. One is that type declarations are typically concise. The other is that they are integrated into the grammar. (The type systems in common use are also declarative and decidable, but once a type system is made powerful enough to express real-world program constraints, it becomes Turing complete.)

Common Lisp has a syntax for extensible declarations, that can be used for type declarations as well as other purposes. Fortunately, it’s optional. Unfortunately, it’s so verbose that I only use it as a last resort — only when I need the program to run faster, not just when I want to record my intent.

Many of the other alternatives to type declarations are similarly verbose. Preconditions (part of Design by Contract) are more expressive than type declarations, and can function as a superset of type declarations too, but saying something simple like “this is an integer” takes more typing as a precondition. As with comments, a type declaration does a little of what preconditions do, and in the cases where either type declaration or a precondition will work, the type declaration is more concise.

(Note that this is a property of the languages that have type declarations, not anything inherent about type declarations and preconditions themselves. Still, that’s what we have to work with today.)

Another example is unit tests. Above, I blithely stated that unit testing was superior to type declarations — and, if you’re able to easily write and run them, I believe this is true. The verbosity of a unit test can be a significant impediment to doing this, though. I’ve noticed that in a language (or library) with integrated unit tests I can use unit testing to find all the errors that type declarations would find (and more). But in a language where unit tests are verbose, I don’t write nearly so many. Regardless of the inherent merits of type declarations and unit test testing, the type declaration you write is more valuable than the unit test you don’t.

Granularity

The reason type declarations are concise is that they’re conventionally integrated into the host language’s grammar. This leads to another advantage: in principle, a type declaration can apply to any term in the grammar — like a comment, but unlike, say, the latest breed of metadata annotation mechanisms. (C# attributes, Java metadata annotations, and Python decorators are all constrained to the class and member level; they can’t be used to annotate a variable or expression.) In practice, most languages attach type declarations to variables instead of values (Common Lisp and Haskell are two exceptions), but this still gives you a finer annotation grain size than you get with metadata. This means, for instance, that a metadata approach to annotating Java or C# method parameters would have to include a new syntax for annotating the parameters of a function, inside an annotation that was attached to the function itself — and this would be more verbose, more complex, and require the maintenance of two duplicate structures (the annotation, and the signature itself). And this means that a problem (such as annotating the SQL types of a function’s parameters) that might better be solved conceptually by metadata, will often have a more widely applicable (to documentation and other requirements) and a more concise solution, and therefore a better practical solution, that uses type declarations instead.

Credits

Some insightful comments on the equally insightful Patrick Logan’s posting The ROI of Optional Type Annotations got me thinking about this.

Addendum

I’ve added a follow-up here.

Visualizing Basic Algebra

Last weekend, I shared some interesting properties of numbers with my kids.

The great thing about explaining something to a non-expert is that you have to actually understand the topic. (This is why making teaching universities and research universities the same actually makes sense.) If you hide behind a formalism, the explanation won’t work. Usually, this means that you didn’t understand why the formalism worked either.

This is why I thought “why are far away things smaller?” was such a great question. “Similar triangles” answers are brittle, and if a tiny error makes far away things come out bigger instead, you won’t catch yourself until you got to the end of the proof.

Some of the interesting properties of numbers are: that (n + 1)×(n-1)=n2-1: that the perfect squares (0,1,4,9,…) go up by successive odd numbers (1,3,5,…); and that the area of a triangular number (1+2+…+n) has a closed form. These properties are easy to show using algebra, but to a child, the algebra doesn’t make sense.

Multiplication and division are grounded in visuospatial concepts, which is why these number theoretical results are easy to understand. What would a proof that stayed grounded in visuospatial concepts look like?

Properties of Addition

Addition is associative:

and commutative:

Multiplication is Commutative

The commutative law is that a×b=b×a. If a rectangle a high and b wide represents a×b , this is the same as saying that rotation is area-preserving:

Or, in the case of three factors, volume preserving:

Distributive Law

Associativity

Difference of Squares

The perfect squares are 0, 1, 4, 9, 16, …. The differences between successive squares are the odd numbers: 1, 3, 5, 7, 9, ….

In algebra, this is because the n2 terms in (n+1)2=n2+2n+1 and in n2 cancel, leaving 2n+1. Here’s why this is true in geometry:

Product of Alternates

1×1=10×2=0
2×2=41×3=3
3×3=92×4=8
4×4=163×5=15
5×5=254×6=24

The product of two numbers adjacent to a median, is one less than the square of the median. For example, 52=25, and 6×4=25-1=24. Algebraically, this is because (n+1)(n-1)=n2-1. Geometrically:

Triangle Numbers

In closed form, the sum of the numbers from 1 to n is n(n+1)/2. One algebraic proof for this uses induction: substitute each of k+1 and k for n, and compute the difference.

There are two geometric proofs. The first requires two cases: one for even n, and one for odd. The second is simpler, but requires a bit of algebra: you need to draw two triangles, and compute twice the area; at the end, you divide by two.

First, the two-case proof, and some terminology. Cut a line at an integer mark as nearly in half as you can. The big piece is the superhalf. The little piece is the subhalf.

For an even number such as 6, the subhalf and superhalf are the same as the half (3). For an odd number such as 5, the subhalf (2) and superhalf (3) differ by one. The subhalf and superhalf of any integer sum to that integer: 3+3=6, and 2+3=5.

Now the first proof. If n is even, take the top half of the triangle and fold it over onto the bottom half. Each row has the same length, because the base increases by one going up, and the flipped top increases by one going down. That length is the width of the original base, plus one from the tip. Since there are n/2 rows, the triangle has the area of a rectangle that is n/2 high and n+1 wide.

For odd n, take the top subhalf of the triangle and fold it onto the bottom superhalf, extending each row except the bottom. The new rectangle is (n+1)/2 high (the superhalf), but only n wide. Multiply these out, and out the same as the first case.

A proof that doesn’t require two cases is to use two triangles. Consider the two triangles, which collectively have the area n×(n + 1):

This is the same, in algebraic terms, of taking the sum [1+n]+[2+(n-1)]+…+[(n-1)+2]+[n+1]=n×(n+1).

Next week: grounded fractions.

Addendum

Hans Martin Kern suggested calling this “Visualizing Basic Algebra.” That’s a much better name, and I’ve changed the title.

Serving Client-Side Applications

[Update 2006: "Model N" never caught on. A few months after I wrote this, Jesse James Garrett coined the term AJAX. This is what the architecture described in this post is known as today.]

In a traditional server-side web application, the server renders a series of views which are downloaded, as HTML, to the client. A client-side web application is an application that is deployed from a server and displays data from a server, but can render a series of views on the client.

I’ve been watching server-side developers try to figure out how to serve client-side web applications for a few years now. Different developers, that is — it doesn’t take years for any individual developer to figure it out. There’s often an initial stumble, which is caused by a mismatch between the obvious way to deploy a client-side web application, and the right way. The right way is simpler, but elusive.

Model 2

A server-side web application generates a sequence of HTML web pages. Each of these web pages represents an updated view of the data model, and sometimes an updated model. The diagram below illustrates this as a series of server requests. (In this example, each request is served from a JSP, but the pages could be coming from Perl, Python, PHP, or raw from the filesystem.) The JSP generates an HTML page, which is displayed in the browser. The generated page includes a link or a form which requests another URL from the server. The response to this subsequent request can come from either the same, or a different, JSP.

This is Server-Side MVC, or Model 2. I actually haven’t shown enough details in the diagram to distinguish this from Model 1 (the code that implements the Model, View, and Controller should be in separate files), but that’s because this is mostly irrelevant to this discussion. If this bothers you, pretend that there are additional files hiding in the server side of the diagram.

Introducing the Client-Side Application

A client-side web application is an application that renders views on the client, not the server. Client-side web applications are generally capable of a wide variety of user interactions without requiring a full update from the server — as opposed to a server-side application, where most user interactions replace the data on the client wholesale. Think GMail or Oddpost, say, or look at the Laszlo demos.

A client-side web application is different from a rich application in that the latter incorporates cinematic effects such as animation, dynamic layout, and media. Client-side and rich go well together for reasons I discuss here, but they aren’t the same. GMail is a client-side application, but it’s not a rich application. The Laszlo demos are both.

A client application might be simply HTML with a lot of embedded JavaScript1. Or it might be, as is the case with Laszlo, Flex, or SVG, an entirely different file type — possibly embedded within an HTML page that forms the initial download.

A server-side web application is delivered as a sequence of HTML pages which are rendered on the server. A client-side application is delivered as a single application file (or at least a single root file), that computes new views or view updates — does its rendering — without replacement, on the client.

For the sake of concreteness, let’s assume the client-side application is a Laszlo application. Not only does this allow me to plug my favorite RIA framework, but that’s where I have the clearest idea of what I’m talking about anyway.

The source code for a Laszlo application is a set of XML files and the assets (PNGs, JPEGs, XML data, and scriptless Flash files) that they include. The deployment set for a Laszlo application is a swf file (the Laszlo executable file), and the set of assets that the application requests once it has started. For simplicity, I’ll illustrate each Laszlo application as a single source file and a single deployment file:

“Model 3″

From the server’s perspective, the most obvious difference between a server-side application and a client-side application is whether the server responds with an HTML file (generated by a JSP) or a Laszlo executable file (compiled from a Laszlo source file). You might think the simplest way to write a client-side application would therefore be to replace the JSPs on the server by Laszlo source files. This will cause the server to generate Laszlo executables instead of HTML pages — voila: a client-side application. I’ll call this Model 3, because it’s an incremental change from Model 2. It’s illustrated in the picture below.

But something is wrong with this picture. Where did the back-end integration and middleware go? How does this work with Struts and CMS? Something is clearly missing. (In fact, the first question about Laszlo from any serious server-side developer is often “How do I use Laszlo with Struts?”)

“Model 4″

What Model 3 is missing is dynamic content generation. That’s what Struts, middleware, etc. are designed to deliver. In server-side programming, the JSP creates a new HTML in response to each request — that’s where the content generation comes in in Model 2. Where can we re-introduce this feature into Model 3?

Laszlo source code looks like HTML (well, XHTML): they’re both XML, and they can both embed JavaScript. It’s tempting to bring back the JSP, but use it to render a Laszlo source file instead of HTML. This source file is compiled on the server, and delivered to the client as an executable. I’ll call this Model 4.

From the perspective of server-side programming, Model 4 looks natural, if a little bulky2. But Model 4 is wrong too.

To see the problem with Model 4, take off your implementor hat for a moment, and think about the user experience — in particular, about page refresh. Page refresh is one of the problems that client-side web programming is intended to solve.

In a server-side application, each user interaction3 results in a page refresh. This is why a server-side application is implemented as a series of HTML pages, instead of just one. This can be easier for the developer, because it unifies a lot of the architecture on the server. But it has a detrimental affect on the user: the user interface is laggy, can’t preview responses at interactive speeds, and requires a full-page refresh (losing a lot of the presentation context) whenever anything significant changes.

In other words, the benefit of a client-side application is that it’s a single-page application. The architecture above throws this advantage away. Model 4 only makes life harder for the application developer, without delivering any compensating benefit to the application user4.

Serving the Client

In a client-side web application, a single web page (or its application equivalent) is downloaded. This application can generate a sequence of views on the client. Sometimes the client application requests a new dataset — typically in XML, or a protocol (RPC, SOAP) that is serialized to XML — but this dataset is read into the client application; it doesn’t replace it.

Here’s the architecture that delivers this. The files on the server include a JSP that generates an HTML embedding page, a Laszlo source file that generates a Laszlo executable that embeds within this page, and a set of JSPs that generate XML datasets. This is a particular implementation of a Service-Oriented Architecture, but in order to emphasize its continuity with Models 1-4, I’ll call it Model N. (I’m leaving myself room for more models in the middle.)

One thing to note about Model N is that the client side is radically different from Models 1-4. Instead of a sequence of pages that replace each other, the application is delivered as a single HTML page that embeds an application. This application in turn consumes a series of datasets.

The other thing to note about Model N is that the server side is very similar to Model 2, or MVC — in fact, much more similar than the failed Model 4 is. Just like in Model 2, the server is implemented as a set of JSPs. The major difference is that instead of rendering HTML, most of these JSPs are rendering XML — or, serializing data to XML. (In many applications, this may mean that some JSPs can be eliminated. A JSP isn’t necessary if it was only transforming a datasource that is already in XML into HTML.)

And last, a caveat: it’s possible to take this too far. I’ve only illustrated the part of a web application that can be replaced by a client-side application. It doesn’t generally make sense to do this with an entire web site. HTML is still best-of-breed for delivering hyperlinked, structured documents, and most parts of a web site are more like documents than like applications. The part of the Model N data flow that I haven’t drawn is at the end of the application, when the server transitions back into a series of HTML-generating JSPs, and the client transitions back into a series of HTML pages.

Notes

1 “a lot of embedded JavaScript”: “A lot” meaning on the order of Oddpost or GMail, say, as opposed to just rollover effects and form validation.

2 “a little bulky”: Model 4 replaces Model 2 and Model 3′s single level of code generation with two: one from JSP to Laszlo source file, and from Laszlo source file to executable.

3 “each interaction”: Aside from local view manipulations such as rollovers and scrolling, and aside from pre-submisssion form editing.

4 “without delivering any additional benefit to the user”: In a client-side application that is also a rich application, you can compile some cool effects such as dynamic layout and declarative animation into each page of your application, but there’s no benefit at the user task level.