Classes as Sugar (was: Renaming ECMAScript 4 for the final standard?)

Brendan Eich brendan at mozilla.org
Mon Mar 24 21:30:11 PDT 2008


On Mar 24, 2008, at 6:45 PM, Mark S. Miller wrote:

> Now on to your real question. Why do I seem to believe that proposed
> ES4 is statically typed? A fair question. Proposed ES4 lies somewhere
> between the simple categories of "statically typed" and "dynamically
> typed". However, rather than finding a happy compromise between the
> two, it mostly combines the worst of these two worlds.

That's a charge you have yet to demonstrate.

It's ironic to read your charge that ES4 is statically typed, just  
after reading Matthias Felleisen in Steve Yegge's blog comparing  
"Proposed ES4" to TypeScheme and criticizing ES4 because it does not  
impose a static type system on JS.

> It pays most of
> the costs of static typing but obtains few of the benefits.

How do you know this?

> And it
> inherits all the costs of having been dynamically typed, but retains
> few of the benefits.

How did you use "Proposed ES4"? Did you write programs in it?

> Benefits of Static Typing
>
> * static assurance that type mismatch errors will not happen at  
> runtime
> * runtime space savings
> * runtime time savings
> * type-based IDE refactoring support (as in IDEA and Eclipse)

Or Flex Builder, which emits a rough subset of "Proposed ES4". Or  
once upon a time ASP.NET emitting JScript.NET.

The same retort (we are operating at a crude level of rhetoric here,  
almost fact-free, but I'll play along) applies to your other three  
bullet points above.

> Costs of Static Typing
>
> * language complexity

And over-minimizing a language imposes a complexity tax on  
programmers using it. This happened long ago with JS1 and it was  
compounded by foot-dragging monopolist misconduct since 1999.

To decide whether to evolve JS or shrink it, you need only look at  
two things: 1) problems JS hackers run into every day, which are not  
solved by more idiomatic functional programming hacks that add cycle  
and space bloat to their libraries and applications; 2) competition  
from other languages in runtimes vying to displace the browser.

> * limit expressiveness to statically checkable type "predicates".
> :int, but no :prime

Not for want of trying: see

http://wiki.ecmascript.org/doku.php?id=proposals:contracts

Still possible in ES5 after we finish ES4.

I would like to point out that PLT-style contracts are *research*,  
and we've been criticized without justification for "doing research"  
in ES4. Why are you promoting contracts when the research has not  
been reduced to efficient practice yet, and AFAIK depends on a module  
system to keep the contracts on the outside of modules, not on the  
inside?

> * two "expression" languages - static type expressions vs dynamic
> value expressions

That's a fair point. In order to support an *optional* static  
checker, the ES4 design restricts type expressions.

Why this is a hardship has yet to be demonstrated, since your  
preferred style of language offers zero type checking at runtime (or  
optionally at compile time). If you are arguing the static side of  
the equation, please cite statically typed languages that allow full  
value expressions as types. I know of research languages, but besides  
having undecideable type systems, they are (again) *research*.

> * multiple partial type theories: nominal, structural, duck, ...

The inclusion of a structural type system is a virtue, since untyped  
objects are common on the web even in the face of the nominal DOM and  
browser object types, and since no one can revise all code entangled  
on the web to use nominal types, at once or even ever.

I don't know what you mean by "duck", it's ill-defined and generally  
unsound when used in other languages. Structural types address the  
latent typing disciplines used in current Ajax libraries, JSON  
schema, etc.

> * verbosity

On the contrary:

// Version 1 of a webmail client, in pure ES3

function send(msg) {
   validateMessage(msg);
   msg.id = sendToServer(JSON.encode(msg));
   database[msg.id] = msg;
}

function fetch() {
   handleMessage(-1);                  // -1 means "get new mail"
}

function get(n) {
   if (uint(n) !== n)                  // JS1: n>>>0 === n
     throw new TypeError;
   if (n in database)
     return database[n];
   return handleMessage(n);
}

var database = [];

function handleMessage(n) {
   let msg = JSON.decode(fetchFromServer(n));
   if (typeof msg != "object")
     throw new TypeError;
   if (msg.result == "no data")
     return null;
   validateMessage(msg);
   return database[msg.id] = msg;
}

function validateMessage(msg) {
   function isAddress(a) {
     return typeof a == "object" && a != null &&
            typeof a.at == "object" && msg != null &&
            typeof a.at[0] == "string" && typeof a.at[1] == "string" &&
            typeof a.name == "string";
   }
   if (!(typeof msg == "object" && msg != null &&
       typeof msg.id == "number" && uint(msg.id) === msg.id &&
       typeof msg.to == "object" && msg != null &&
       msg.to instanceof Array && msg.to.every(isAddress) &&
       isAddress(msg.from) && typeof msg.subject == "string" &&
       typeof msg.body == "string"))
     throw new TypeError;
}

// ES4 version of same:

type Addr = { at: [string, string], name: string };
type Msg = {
   to: [Addr], from: Addr, subject: string, body: string, id: uint
};
type MsgNoId = {
   to: [Addr], from: Addr, subject: string, body: string
};

function send(msg: like MsgNoId) {
   msg.id = sendToServer(JSON.encode(msg))
   database[msg.id] = msg wrap Msg
}

function fetch()
   handleMessage(-1);

function get(n: uint) {
   if (n in database)
       return database[n];
   return handleMessage(n);
}

function handleMessage(n) {
   let msg = JSON.decode(fetchFromServer(n))
   if (msg is like { result: string } && msg.result == "no data")
       return null
   return database[msg.id] = msg wrap Msg
}

$ wc /tmp/*way
       30     102     688 /tmp/newway
       47     177    1302 /tmp/oldway
       77     279    1990 total

ES3 code shrinks when rewritten in "Proposed ES4".

> (inside Google, we've expanded to a 100 column limit for
> Java generics)

(What has that to do with anything but Java as used inside Google?  
There's no point in arguing about behind-the-firewall non-evidence.)

> * inappropriate for casual scripting audience

ES3 is a subset of "Proposed ES4" and supports evolution and code  
migration, along with single specification and implementation  
(critical on small-device browsers). Beliefs about "Proposed ES4" and  
what is appropriate for "casual scripting audience" members need  
demonstration, not assertion.

> Benefits of Dynamic Typing
>
> * lambda abstraction / objects is all you need

We know that lambda is all you need. Why bloat things with objects?  
For that matter, why speak English instead of Standard Business  
English, the subset Guy Steele made up for his "Growing a Language"  
OOPSLA '98 talk, or Esperanto?

> * Tennent correspondence
> <http://gafter.blogspot.com/2006/08/tennents-correspondence- 
> principle-and.html>

Java closures are years late and mega-dollars short of the mark,  
compared to first-class functions and closures. Perhaps you agree?

> * all abstractions first-class, composable

That's our goal too, but you'll have to study "Proposed ES4" to show  
us where we've failed, instead of citing irrelevant Java non-evidence.

> * simple meta-interpreters can enable powerful meta-programming

I'm not sure what you mean. Could you give an example? I wrote a meta- 
circular ES3 interpreter in a superset of ES3 (SpiderMonkey extended  
to host and boostrap Narcissus), but I don't know what you are  
implying cannot be done by ES4, or can be done better, or  
exclusively, by ES3 here.

> * syntactic simplicity supports other metatools: minifiers, lints, ...

Syntax is also user interface, and again minimizing it too much makes  
programming harder. If this bullet were overriding, we'd be using S- 
exprs.

> * rapid prototyping

Done all the time using ES3 and "Proposed ES4" approximations and  
piece-wise implementations, some already shipped (JS1.8 in Firefox 3  
beta), some much closer to ES4 being implemented now.
>
> Costs of Dynamic Typing
>
> * runtime space and time costs

Space costs are indeed an issue.

I am happy to ignore time costs, and stipulate that they can be  
reduced to noise by clever optimization techniques for most cases.

> * less static knowledge of dynamic behavior

A lot less ;-).

> Benefits of Soft / Gradual Typing
>
> Proposed ES4 and some dynamic languages share some of the advantages
> of soft typing systems. A soft typing system is, essentially, a
> dynamic typing system with a convenient syntax for declaring type
> checks that should be checked at runtime.

Have you read anything about 'like' types in "Proposed ES4"'s  
overview document or evolutionary programming tutorial?

> * Better documentation of expected interfaces - better project  
> coordination
> * Fail-fast runtime behavior
> * easy transition from rapid prototypes to production

These bullets all arguably apply to "Proposed ES4".

> Benefit of Soft Types absent from Proposed ES4 gradual types:
> * type "predicates" are any runtime test expressible in the language

This is a selective argument, since (a) we have structural types and  
'like', which cover common cases (common meaning probably > 80%, but  
to be demonstrated, I admit); (b) we can add contracts in the future  
if the need arises *and the research is "done"*.

>                                                          Classes as  
> Sugar
>
> Given something like the __createProperty__

(__defineProperty__ in the latest spec, FYI.)

> we
> give integrity to this use of nominal typing. The proposed ES4 type
> declaration syntax could then expand to simple nominal type checks:
>
>     var x :C = ...;
>
> expands to
>
>     var x = Object.cast(C, ...);
>
> where
>
>     Object.cast = function(type, value) {
>       if (value instaceof type) { return value; }
>       throw new TypeError(...);
>     }

What is the point of all of this? I can map ES4 to ES3 too, and so  
can others -- some are even building translators. But programmers  
should (a) not have to do this by hand; (b) not have to procure and  
use an offline ES4 to ES3 translator to reap the benefits of ES4,  
except for the transparent debugability (instead, they get leaky-unto- 
flooding-abstraction hell).

> One further suggestion to make the class syntax more convenient: Get
> rid of separate constructors. Instead, allow parameters after the
> class name which are then in scope in the class definition.

This was considered too, see:

http://wiki.ecmascript.org/doku.php?id=discussion:nullability#example

I like the first part -- constructor parameters in the class head.  
The odd-looking special form "function Foo { ... }" for the  
constructor code loses compared to an intact constructor function,  
though.

> With the expansion above, we seem to have only implemented a subset of
> proposed ES4 providing only nominal types. Sure, it's simpler. But how
> is it more expressive? Unlike proposed ES4 classes, the expansion
> above is lexically nestable and multiply instantiable. This provides
> the flexibility of a runtime trademarking system, where the program
> can create as many trademarks as are dynamically needed at runtime.

I favor allowing classes in "Proposed ES4" to be nested in functions,  
for what it's worth. Since we aren't done yet, perhaps this will come  
to pass, although "lexical nestability" is not a burning issue in  
general.

"Multiply instantiable" is indeed relevant on the web with various  
window objects and browser-specific sandbox objects hosting  
instances. We can do better, but so could your example. There's no  
reason to force everyone to write, or translate to, a minimally  
extended ES3 and suffer the performance and debugging consequences.

> The key to the correctness of the above code is that every call to
> makeBrand create a distinct nominal Envelope type,

In ES4 as proposed you can use eval to make distinct nominal types.

/be




More information about the Es4-discuss mailing list