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.
Posted: December 31st, 2004
Categories:
Essays,
Software Development
Tags:
Comments:
11 Comments.
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:
^2.png)
Product of Alternates
| 1×1=1 | 0×2=0 |
| 2×2=4 | 1×3=3 |
| 3×3=9 | 2×4=8 |
| 4×4=16 | 3×5=15 |
| 5×5=25 | 4×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):
.png)
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.
Posted: December 5th, 2004
Categories:
Math Education,
Visualizations
Tags:
Comments:
9 Comments.
[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 JavaScript. 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 bulky. 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 interaction 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 user.
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
Posted: December 5th, 2004
Categories:
Essays,
Software
Tags:
Comments:
11 Comments.