JS2 Design Notes

Goals

Here are some design notes for JS2, starting with my goals, shared in large part by ECMA TG1 for ECMA-262 Edition 4:

  1. Support programming in the large with stronger types and naming.
  2. Enable bootstrapping, self-hosting, and reflection.
  3. Backward compatibility apart from a few simplifying changes.

(Goal 2 implies many things beyond what is discussed in these notes.) Non-goals, again shared (mostly!) by ECMA TG1 going back to Waldemar’s Edition 4 drafts:

  1. To become more like any other language (including Java!).
  2. To be more easily optimized than the current language.

Types

In JS today, every expression has a type, as specified by ECMA-262 Edition 3
Chapter 8. The visible types, by their spec names, are Undefined, Null, Boolean, Number, String, and Object. These types are disjoint sets of values:

Undefined = {undefined}, Null = {null}, etc.
Int32 and UInt32 are subsets of Number (IEEE-754 double precision) subject to
different operators from Number, and they appear only in the bitwise operators,
Array length, and a few other special cases.

Edition 3 Chapter 9 defines somewhat ad-hoc, mostly useful conversion rules between types. Chapter 15 contains constructor specifications that may also convert according to the Chapter 9 rules, or ad-hoc variations on those rules.

One oddness to JS1: the so-called primitive types Boolean, Number, and String each have a corresponding Object subtype: Boolean, Number, and String respectively. When a primitive value is used as an object, it is automatically “boxed” or wrapped by a new instance of the corresponding object subtype. When used in an appropriate primitive type context, the box/wrapper converts back to the primitive value.

For JS2 and ECMA-262 Edition 4, we would like to use modern type theory to avoid the pitfalls and contradictions of less formal, ad-hoc approaches. We define a lattice of all type value sets induced by the set contains
subset
relation, in order to:

  1. Define new operators to enable programmers to test and enforce invariants using type annotations (goal 1).
  2. Let users define their own types by writing class extensions that can do anything the native classes can do (goal 2).
  3. Eliminate primitive types and boxing, coalescing each Edition 3 primitive type with its object wrapper (simplifying exception from goal 3).
  4. Provide nullability and rationalize Undefined (goals 1 and 3).

The lattice is as follows, with arcs directed downward by default (arcs directed otherwise have an arrowhead showing direction):

___⊤___
/       
/         
Void        Object?__________
/               
/                 
Null<----String?    Object__________
     /   |           
   /    |            
String  Number  Boolean  User...
(double)
/  |  
/   |   
int  uint  ...

⊤ is the top type, not named T in the language. Edition 3’s Undefined is renamed to Void, as in Waldemar’s Edition 4 drafts.

For all object types t, there exists a nullable type t? = t ∪ Null. Only Object? and String? are shown above, but every object subtype is nullable. Note that this is just a specification notation; we have not committed to adding the ? typename suffix for nullability to the language.

The User… type stands for a hedge of user-defined type trees. I’ve left out Array, RegExp, Date, etc., because they can be thought of as
user-defined Object subtypes. Also, not all proposed numeric types are shown (not all are subtypes of IEEE double).

Type operators

Given a value and a type, you can ask whether the value is a member of the type’s set using the is relational operator:

v is t ≡ v ∈ t's value set ⇒ Boolean

A class defines an object type, and class C extends B {...} defines a subclass C of base class B. All values of a subclass type are members of its superclass type, so (new C is B).

Given a value of unknown type, the as relational operator coerces (or downcasts) the value to the type, resulting in null if the value is not a member of the type:

v as t ≡ (v is t ? v : null) ⇒ t?

So, e.g., undefined as Object === null — this shows how the type of an as t expression is t?.

Given a value of arbitrary type, the to relational operator converts the value to be a member of the nullable extension type, or throws a TypeError exception.

v to t ≡ (v is t ? v : v converted to t) ⇒ t? or throw TypeError

The to operator may result in t rather than t?, at the discretion of the class implementing t (e.g., null to Boolean === false). A class may define its own to operator using the following syntax:

class C extends B {
...
function to C(v) {...}
}

We will redefine the type conversions specified variously in Edition 3 Chapters 9 and 15 in terms of the to operator applied to the native classes.Our current thinking is that to conversions follow Chapter 9, except for any of (Null ∪ Void) to (String ∪ Object), which all result in null, not "null", "undefined", or a TypeError throw.

Type annotations

Testing and enforcing invariants using these type operators in expressions governing control flow is sometimes useful, but often tedious, error-prone, and bloaty. We wish for typed declarations that enable the language implementation to do the testing and enforcing for us. Therefore for each of the three type operators is, as,and to, there is a corresponding type annotation that may be used with var, const, and function declarations to specify type:

var v is t = x ≡ if (!(x is t)) throw TypeError; var v = x
var v as t = x ≡ var v = x as t
var v to t = x ≡ var v = x to t

The initializer is optional as usual; if missing, a sane default value for the annotated type is used. For all assignments v = x following such a type-annotated variable declaration, the production on the right of ≡ above, stripped of var, is evaluated. Function formal parameters and the function’s return value may be annotated similarly:

function f(a is int, b as Object, c to String) is Number {...}

Type annotations are optional. To support strict options that require every declaration to be annotated, * may be used for ⊤ (the top type), e.g. var v is *, which is equivalent to var v. Note that * is used differently for E4X, but its meaning as ⊤ is unambiguous in type operator and annotation right operand contexts.

In a nutshell, is t annotations insist on type t and defend against null and undefined (no more “foo has no properties” errors; with static analysis, an error that can’t be avoided at runtime can even be reported at compile time). as t annotations enforce (is t)-or-null invariance. And to t annotations convert according to cleaner, class-extensible rules.

Coming soon

In the next update, I’ll list the small number of incompatible changes to Edition 3 that we are considering. In a subsequent item, I will discuss stronger naming mechanisms to support programming in the large.

New Roadmaps

Mozilla is a huge project, now cursed with success. It did not start that way. To think about where to go, we should mull over how we got here.

Over the years since the first major roadmap, I’ve tried to steer the project toward the shortest path to the next port of call that was not going to leave us cannibalizing the ship and ourselves, for want of supplies and fresh crew (more contributors). Doing this without running out of stores and existing crew along the way required good luck, along with a hard-nosed attitude about what to keep and what to throw overboard.

Some paths were long, for instance the reset in late 1998 around Gecko, XPCOM, and an XPFE. This was a mistake in commercial software terms, but it was inevitable given Netscape management politics of the time, and more to the point, it was necessary for Mozilla’s long-term success. By resetting around Gecko, we opened up vast new territory in the codebase and the project for newcomers to homestead.

More recent journeys to better ports were much more in tune with the Internet zeitgeist. The best example was FIrefox as the new model application.

Firefox changed everything for us, for the industry, and most important, for browser users. We have heard repeatedly from people who feel as though their computers are friendly tools again, instead of spyware-ridden zombies infected via the default browser. These folks now see the web as fun and empowering, not static or even degrading over time.

Now, having given the default browser a little competition, and helped advance the causes of simple yet powerful UI, web content standards, and user innovation toolkits and networks such as XUL extensions, their own extensions (e.g., GreaseMonkey’s user scripts), and AJAX/DHTML/whatever new-style web apps, we must journey again to an even better place, with another hundred million downloads, and even more users worldwide.

We see the Web outpacing traditional desktop and operating system software development. Great web services and mashups abound in the new, over-hyped era. In spite of the hype, these new Web apps usefully point to the immense, hardly-realized value of people connected by the Internet.

Firefox is “just a browser”, but it has an evolving role to play in this world. The browser is a platform, as threatened in 1995 and dismissed with the death of Netscape. Yet every web app is hobbled by stagnant content standards from five or ten years ago. There is no good reason for this.

There is no reason why browsers should not innovate, along with plugins, applications, and operating systems, to support modern hardware-assisted graphics, dynamic programming languages, and richer content types. The reach of the web still favors the browser over any particular OS or non-Web platform.

Therefore in order to innovate nearer to “Web time” on the Firefox user interface and extension fronts, we propose to develop the front end of Firefox 2 and the platform for Firefox 3 concurrently, with Firefox 2 releasing in less than a year. The details have been drafted in a product roadmap that Chris Beard has written. (Many of you know Chris as an active driver and product manager in the project since Firefox 1.0 launched. He’s been a huge help, and I’m very grateful for his contribution here.)

The back end code, Gecko and other modules such as the JavaScript engine, will occupy the Mozilla source tree “trunk” development during the Gecko 1.9 milestone, which is already under way. The major efforts are to:

  • rearchitect our rendering layer to support hardware-accelerated graphics;
  • selectively improve architecture in the layout engine, to fix longstanding bugs;
  • improve web author productivity with better content languages and debugability;
  • move XUL to the next level in concert with the rendering and programming improvements;
  • make XULRunner the embedding vehicle for Firefox and all modern XUL applications.

Mike Shaver and I have gathered together the requirements and plans of many folks in a new platform roadmap.

These roadmap drafts are rough, and although much work has already been done in the directions outlined by these documents, much more remains, and our plans will change as needed. But plan we must, perhaps more than ever in the project’s history. We have reached the big time now, and there is no turning back or coasting along.

Your comments are welcome.