Serving Client-Side Applications 11

Posted by Oliver on December 05, 2004

[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.

Web MVC 3

Posted by Oliver on August 27, 2004

The Model-View-Controller (MVC) architecture is a standard architecture for interactive applications. In client-server programming, the MVC components are distributed across at least two nodes of a network. This leads to a set of choices about where to deploy each component of the architecture. One solution is the traditional server-based MVC model. Another is the Rich Internet Application (RIA) model. In a real-world application with client-side validation, these are more similar than they might seem.

Desktop MVC

In an interactive application, there is typically a domain model, code to present the model to the user, and code to act upon the model in response to the user manipulation of input devices such as the keyboard and mouse. For example, in a word processor the domain model is the document, which contains entities such as paragraphs, spans, and styles. The system presents the document to the user as (for a sighted user) glyphs rendered as pixel patterns, and interprets keystrokes and mouse actions as edit, formatting, and control commands.

Smalltalk-80 introduced the Model-View-Controller (MVC) architecture for structuring this type of application. MVC separates the maintainance of the domain model (the Model), the presentation of the model (the View), and the interpretation of user input (the Controller).

Model-View-Controller Control Flow2

Together with the User, the components of an MVC architecture form two cycles of data and control flow:

  1. The View Cycle: User -> Controller -> View -> User. The user manipulates an input device — for example, by clicking the mouse button, while the mouse cursor is positioned in the down region of a scroll bar. The Controller responds by sending a message to the View (e.g., by invoking view1.scrollDown()). The View updates its state (it adjusts the value of view1.xoffset), and updates the information that it presents to the user (calls bitblt on view1’s screen area, and invokes the draw() methods of views that intersect the accumulated damage region).
  2. The Model Cycle: User -> Controller -> Model -> View -> User. The user manipulates an input device in a different way or in a different context — for example, by pressing control-D while an entry from a list of contacts is selected. The Controller updates the state of the Model, the view updates to match the model, and the user is presented with the updated view. For example, when the user selects the Delete item from a menu, code in the Controller might invoke view.deleteRow(), which invokes view.currentSelection.delete(), where view.currentSelection is a Model object and view.currentSelection.delete() is a Model method. The delete() method changes the Model data, which notifies these objects’ Listeners, say by invoking their registerChange() methods. The set of Listeners includes View objects, which respond to the invocation by updating the display.

In a desktop application, the Model, View, and Controller run on the machine that the user interacts with. This kind of application is easy to write (or, as easy as the domain model allows); it easily takes advantage of client platform features such as dialog boxes, offline storage, and drag and drop; and it’s responsive. The disadvantages of desktop MVC architecture are the disadvantages of desktop applications in general: they are difficult to deploy, they tend to be platform-dependent, and they are generally limited to manipulating data on the local file system3.

Web Application Programming

In a web application, the server software runs on a server, and the user interacts with browser software running on a client. The Model’s data is stored on the server. The user interacts with software running on the client.

Client-server programming is difficult because the program is distributed across more than one network node. Some code has to run on the client to interact with the user. Other code has to run on the server to interact with the data store.

In the case of a web application, the code that runs on the client includes the user agent, or browser. The distributed nature of a web application creates two challenges. One is that the application has to render data from the server into a context where it can be displayed on the client. The other is that information about user events has to get back from the client to the server.

The architectural decision about client-server MVC is which components to deploy to which nodes, and how to manage the communication between them. Should the View and the Controller run on the server, or the client? What about the Model?

There’s a variety of ways to make these decisions. One solution is Server-Side MVC (below). Another is the Rich Internet Application architecture (also below). But all the solutions have some elements in comment, because of the following ground rules:
# At least part of the Model has to run on the server. This is because the Model is responsible for maintaining the integrity of the business objects, and has to be immune from client spoofing. It also sychronizes changes requested by simultaneously executing clients if the changes are bottlenecked through code that runs on the server, although in a clustered server environment this doesn’t come for free.
# The user has to be able to see the information presented by the View. This means the View, no matter where it runs, ultimately must control the display of this information on the client.
# The Controller must be able to receive events that represent user activity.

Server-Side MVC

In Server-Side MVC, the Model, View, and Controller run on the server. The View generates a representation of a presentation. This representation is downloaded to the client, which displays it. User interactions with the client are sent back to the server, where they generally result in the generation of a new presentation, which is downloaded in turn, and updates the previous presentation. This is the view update operation.

Server-Side MVC

In a Server-Side MVC web application, the View generates an HTML page, which is sent to the client as an HTTP response. The browser, on the client, encodes user events as the URL, query, and POST parameters in an HTTP request. The Controller, on the server, interprets this information as Model or View changes, and routes the request to the View, which responds with another HTML page. The view update operation in a web application is implemented as wholescale replacement of the previous view by the new view. In a browser, this is the page refresh.

In the Java community, “MVC” means “Server-Side MVC“. In this context, (Server-Side) MVC is also called Model 2. (Model 1 was a server-side application without the architecture.) JSP, Struts, and PHP are all specific technologies that can be used to implement Server-Side MVC.

The advantage of Server-Side MVC for the developer is that if the Model (aka business logic) is running on the server anyway, this architecture allows the developer to use the same language, libraries, and data structures to write the rest of the application, and local procedure calls to communicate between components.

The disadvantage is that the quality of the user experience is limited by three factors:
# View updates in Server-Side MVC are more coarse-grained than they are in a desktop application. A Server-Side MVC application can’t generally update just part of a view (or an interior node in a view hierarchy); it has to create an entirely new view and replace the old view by the new one, even when the new view is almost identical to the old one10. The user experience correlate of this limitation is the ubiquitous full-page refresh in Server-Side MVC applications.
# User events in Server-Side MVC are more coarse-grained than they are in a desktop application. Multiple client-side user events, such as a series of clicks and keystrokes, are aggregated into a single HTTP request that summarizes their effect. This has implications for the kind of feedback that the Controller is able to give; it’s why direct manipulation such as drag and drop is impossible in this architecture, and why client validation is difficult.
# The previous two problems are the special cases of the problem that the bandwidth of communication between the user and the application is low. The latency of communication is also high, compared to desktop interactivy — any Model cycle, or View cycle that involves a page refresh, is an expensive and offputting activity.

Server-Side MVC is the path of least resistance towards developing an application that provides access to an existing Model implementation. It allows the incremental expenditure of developer resources towards a limited degree of user interactivity. For many applications, only limited interactivity is required. This is especially true for non-consumer web applications: both the users and the organizaton would rather have developer resources go towards back-end improvements and new business features, if these resources are coming out of the same pool that improvements to the user experience would come from.

Server-Side MVC: Under the Hood

Let’s look again at how the user interacts with a Server-Side MVC application. The View in a Server-Side MVC application isn’t actually displaying information on the user’s screen or to the user’s screen reader; it’s creating data which will instruct the user’s browser to display this information. The Client isn’t reading the state of the input devices; the browser is reading this state and summarizing it into HTTP requests.

The diagram above hid this complexity by conflating the User and the User Agent, and placing them both inside the computer. (That’s why the User was inside the computer in the Server-Side MVC diagram.) But the human user doesn’t interact directly with the HTTP and HTML protocols. There’s actually an additional piece of software — the browser — mediating the interaction of the user with the server component of the application. The browser has its own Model, View, and Controller. The browser’s Model is the HTML page. The View renders the page to the screen or screen reader, and the Controller interprets the keyboard and mouse events against the input focus state and the position of rendered elements (such as links and form elements) within the browser window.

Server-Side MVC unpacked (slightly)

This is still (a variant of) an MVC architecture, it’s just that the View and Controller are each split between the client and the server. The high-overhead HTTP request/response transactions occur within components: the back-end View communicates via HTML to the front-end View, and the front-end Controller communicates via HTTP to the back-end Controller.

Cycle Time

There’s an additional complication, which is that now there are two View cycles. User actions that don’t involve a page refresh, such as scrolling the page or choosing a new form element, initiate a Client View Cycle. Actions that require a page refresh, such as paging to the next ten items in a list, or searching for products that contain the term “AAC”, initiate a Server View Cycle. Neither of these actions changes the Model — they’re both View cycles, not Model cycles — but they require different kinds of changes to the View.

Cycles that cross the client-server boundary — the Model Cycle, and the Server View Cycle — are more expensive than cycles that are encompassed within a single node.

The Client Thickens

The final step in fleshing out the diagram above is to account for input validation, such as checking that a credit card number has the right syntax or that input field co-occurrence restrictions hold (if menu item 1 is selected, then input field 2 must be non-empty). Given the expense of a View Cycle that involves the server, it’s desirable to do as much input validation on the client as possible, to avoid the page refresh. Since the input is validated against the model schema, this requires downloading a little bit of business logic to the client.

Server-Side MVC unpacked

We could draw this as a special case of single-Model MVC, just like we did when only the View and the Controller were split between the server and the client. The Model in this interpretation contains multiple components: a server-side component which runs on the server, and at least one client-side library which the View links into the HTML page that it downloads to the client.

This isn’t a thick client, or a Rich Internet Application. The only code running on the client is the browser, and whatever the current page includes. This is still the world of Server-Side MVC, as produced by JSP, Struts, CGI, or other server-side technologies that generate HTML+JavaScript. All that’s changed is that the architecture diagram now reflects (more of) the software’s full complexity.

That complexity includes the real world application of what are still research topics in academia: staged programming for multiple-target code generation (Mac IE, Windows IE, Netscape, Mozilla, Safari, Opera), process migration, and (usually manual) CPS conversion to maintain program state across pages. No wonder web programming is hard!

RIA Architecture

For comparison, here’s an RIA architecture. In this case, the RIA includes a domain model, in order to support client interaction.

Conclusion

When I started working on RIAs, I thought I’d find the comparison between RIAs and conventional server-side applications would look like this:
|Server-Side MVC|RIA|
|||

These diagrams makes RIA architecture look more complex than server-side applications. But it turns out that server-side programming is plenty complex, it’s just that the complexity isn’t represented in the left hand diagram. That diagram conflates the User and the User Agent. It doesn’t take into account interactions between the User and the User Agent, but takes them as a monolithic whole. This makes the surface architecture simpler, because the architecture isn’t at enough level of detail to represent the kinds of interactions that determine the user experience, which include the user’s interactions with the browser.

These diagrams also simplify the architecture of (many) RIAs, which need at least some of the model in order to support RIA functionality.

Once the architectures are fleshed out, the comparison looks more like this:

Server-Side MVCRIA

This makes the RIA architecture look simpler than Server-Side MVC, because it’s only got one view.

Let’s return from architecture astronaut space to earth. The RIA architecture may look simpler on paper, but it can still be a more complicated deployment story. It’s only simpler overall if there’s a significant amount of client-side programming anyway.

Notes

1 There’s a middle ground between changing the Model and changing the View, where the user previews a set of changes, in a dialog box or a multipage web form, before committing them with an Okay button or Order button. This is where Undo and the Back button break down. However, the distinction is generally clear enough to organize an architecture around.

2 This may not be your father’s MVC. Sometimes the User is omitted from the diagram. (The user is integral to the control flow, but isn’t part of the software architecture, after all.) Sometimes the arrow from Controller to View is missing. (Presumably you can’t page or scroll in these applications, or else the state of the presentation is considered part of the Model.) Sometimes there are extra control flow or dependency arrows; and sometimes there are extra components, representing the Store that backs the Model, or who knows what else. But none of this variation makes a difference to what I want to talk about in this post, which is what happens when there’s at least a Model, and Controller, a View, and a client and a server.

Google for MVC
MVC Sampler

3 Just because the Model runs on the user’s desktop machine, doesn’t mean that the Model’s Store is the local file system. That’s just the path of least resistance, and the typical use case for a desktop application. Putting the store somewhere else is an uphill battle for a desktop application, but comes for free with a web application. Conversely, supporting interactivity (e.g. responsive feedback, direct manipulation) is an uphill battle for a server-application, and comes easily with a desktop application.

Test versus Type 22

Posted by Oliver on August 20, 2003

A lightweight language such as Python or JavaScript fits a lot of program design into a small amount of source text. A heavyweight language such as C++ or Java uses more tokens to express the same design.

Many people (Guido van Rossum, Bruce Eckels , Steve Ferg) report that working in a lightweight language is about five times more productive than working in a heavyweight language. This matches my personal experience. Lutz Prechelt1 reproduces this difference within a research setting.

It’s easy to make up reasons why it’s easier to work with shorter sources. There’s less data entry, fewer places to make mistakes, and it’s easier to read the programs. A program in a heavyweight language has a lot of boilerplate that doesn’t add information about the program’s design or intent. Eliminating the boilerplate seems like a clear win.

But what about source text that does add information about the program — in particular, what about types? Removing type declarations from a program also makes it shorter, but it removes information that the compiler and runtime can use to verify the program. Doesn’t this make the program less reliable? Or, given a reliability goal, doesn’t it make it take longer to reach that goal?

From the anecdotes and studies above, it looks like this isn’t actually the case. I’d like to look at why.

1 An empirical comparison of C, C++, Java, Perl, Python, Rexx, and Tcl for a search/string-processing program, Lutz Prechelt. See especially figure 16.

Two Types of Types

An Explicity-Typed Language (ETL) is one in which functions and variables must be declared with type declarations. (These type declarations are conventionally called manifest types.) Java and C++ are ETLs.

An Implicitly-Typed Language (ITL) is one in which type declarations are not necessary. Some ITLs, such as JavaScript 1.5 and Python, lack type declarations altogether. In other ITLs, such as Dylan, Haskell, JavaScript 2.0, and JScript.NET, type declarations are optional, and can be added at any point during program development.

An ITL isn’t necessarily a weakly typed language. (In a weakly typed language, type mismatches are never detected. C++ is weakly typed where it uses with static casts.) Python is an example of latent typing: types are always checked, just not at compile time.

Neither is an ITL necessarily a dynamically typed language. Haskell and ML are examples of statically typed languages where the type declarations are often optional, but the types are known at compile time. The compiler infers variable and function types from the source, just as a Java or C++ compiler infers the types of individual subexpressions.

(This discussion is condensed from the appendix to Steve Ferg’s excellent article comparing Python to Java.)

No Pain No Gain?

It’s common knowledge that it takes longer to get a program in an Explicitly-Typed Language to first clean compile, because it takes time to get the type declarations right. Initial program development consists of editing type declarations as well as non-declaration text (the expressions, control flow, and program structure), followed by a block of time to get the program to first clean compile. The white area below represents work on the non-declaration portion of a program; the gray stripes represent work on type declarations.

In order to show the division between time spent on type declarations and time spent on other program text more clearly, I’m going to slide the type declarations to the right:

This picture appears to show that it takes more time to develop a program that contains type declarations. The received wisdom among ETL fans is that what you get for this extra time is error checking. ETL development takes longer, but gives you a more robust product. The equivalent ITL development effort (which isn’t shown here) would be even longer, because it would include debugging time to catch the problems that the compiler didn’t. In fact, some of these problems might not show up until the product is in the field.

Tests versus Types

Test-Driven Development (TDD) is a development methodology where the test cases are written first, and code is added to the program proper only as necessary to implement correct behavior for test cases.

TDD advocates have noted that if you write your test case first, you also get error checking. In fact, you get better error checking than you do with explicit types, because the tests are tailored to the code paths, the edge cases, and the specific problem domain. After all, the only kinds of errors that explicit typing catches are those of the kind where you used a string in integer context. Programmers who have used implicitly-typed languages can attest that these aren’t the kinds of errors that are hard to find. In fact, a lot of the work to make an ETL program compile is work that’s only necessary to get around how little to compiler knows about the runtime types.

The comparison of an ETL, where the extra work goes into typing explicit types, and an ITL with TDD, where the extra work goes into test cases, looks like this:

Let’s say you could start with a program without type declarations. Would you rather add type declarations, which catch a certain class of problems at compile time? Or would you rather add test cases, which in a strongly typed language catch this same class of problems at runtime, and catch problems that the type system can’t express as well?

But why not use TDD with an ETL?

Tests Plus Types

Here’s what happens when you use TDD with an ETL. You’re taking that first picture, and multiplying it by two: once for the test cases, and again for the program itself.

Robert Martin compares this TDD to dual-entry bookkeeping. Explicit types are another form a dual-entry bookkeeping. Using both explicit type declarations and test cases to validate is triple-entry bookkeeping. (And the time spent writing explicit types could have been spent on more test cases, or more of the program.)

But it’s actually worse than triple-entry bookkeeping, because an ETL requires that you use explicit type declarations in your test code too. Not only does the program itself cost more to write, but so do its test cases. It’s quadruple-entry bookkeeping: one of the three entries has two entries itself.

This is why even software written in an ETL is often tested with an ITL test harness. If you can afford to work in two languages, and if the test harness and the debugger are well-enough integrated that you can write the test cases first and use them for TDD, this works. (It brings the cost of using an ETL back down to triple-entry bookkeeping.) But there’s one more gotcha.

The Type Tax

The problem is that once type declarations are part of the source text, they’ve got to be maintained along with the rest of the code. The earlier the type declarations are added, the greater their contribution to the maintenance cost.

In a waterfall model of code construction, where all the code is written, brought to clean compile, and works the first time, this doesn’t matter. In incremental development, where compilable program is debugged or extended to handle additional features or test cases, the cost can be high.

Types are similar to comments, test cases, and end-user documentation in this way, except that types are coupled more closely to each line of source text. Factor out a variable from an expression, and the method comment and end-user documentation can stay the same, but a new type declaration is required. This tight coupling makes the maintenance tax on types even higher than it is for other program annotations, and the tradeoff for when to start adding them, if at all, is different.

Since the cost/benefit ratio of type declarations can be different at different points in program maturity, one style would be to add them late in program development (for performance reasons, or for their documentation benefits to future maintainers). This style of development is possible in a language with optional type declarations, such as Dylan or Haskell. That’s what the first picture on this page shows. ETLs such as C++ and Java simply don’t allow this.

Updated 9/25/2003 to rename “write types” to “edit tpes”, and to add a picture showing type declarations interleaved on a finer grain.