Insurrection (was: ES4 draft: Object)

# Mark Miller (18 years ago)

On Mon, Mar 10, 2008 at 8:11 PM, Jeff Dyer <jodyer at adobe.com> wrote:

On 3/10/08 5:40 PM, Waldemar Horwat wrote:

I'm dealing with a serious insurrection of folks who believe that the ES4 working group has a bad attitude, based on Brendan's public comments and responses to issues like this one. They're quite visible.

No doubt the WG has attitude. There are strong personalities involved and we have had our share of knocks. Sometimes that shows. You of all people should understand why. But please do share more about the insurrection you are dealing with. Maybe then we can be more sensitive.

I hope Waldemar is not counting me as a member of that insurrection. My problem with the ES4 proposal has nothing to do with the attitude and personality of the ES4 WG members. Quite the opposite. I have come to like and respect those of you I have come to know. Since Brendan is mentioned explicitly above, I'll mention the high regard I've come to have for Brendan in particular. I have also enjoyed every encounter I have had with the group as a whole.

"A vague disclaimer is nobody's friend." --Willow Rosenberg, Buffy episode: The Initiative

"True friends stab you in the front." --Oscar Wilde

I do not wish to offend anyone. I know I am speaking harshly about work that many here have invested in heavily. But in the interests of clarity, I will speak plainly below. I hope the discussion will continue from here with its customary civility.

I have serious problems with the proposed ES4 design -- to the point that I have a hard time taking it seriously. I hope and expect that it will be crushed by its own weight. I fear that it might become widely adopted, and we will instead be crushed by it. In any case, I have decided that my energies as a new EcmaScript committee member are better spent on the ES3.1 WG, as the other members of that WG seem similarly inclined to look for the smallest reasonable language that meets our goals es3.1:es3.1_goals.

The current fad in language design is expressive static type systems. As a result, several recent type systems have grown eat their language. Java 1.5 is a particularly nasty example. Simple bits of Java code become so overwhelmed by complex type declarations as to obscure the logic of the code itself. Proposed ES4 suffers from the same disease, though it has not (yet) come down with as bad a case as Java.

I applaud Jeff's and Lars' recent document of proposed deferrals from proposed ES4. After reading this document carefully, I was then able to reread the proposed ES4 overview document and imagine the smaller language they are suggesting. This language I still believe to be too large, but at least it would now be small enough to be worth criticizing.

ES3 has several abstraction mechanisms:

  • lambda abstraction, which it gets approximately as right as Scheme!
  • objects as a generalization of records, which has some pros and cons
  • prototype-based sharing of common behavior, which is used almost exclusively by JavaScript programmers to express only class-like patterns.

Altogether, ES3 has many virtues and many problems. One of its great virtues is its almost perfect support for lexical nesting. Virtually any thisless construct that could appear at top level can also appear within a nested lexical context with the same meaning. ES3 also avoids the CommonLisp trap of multiple namespaces, instead siding with Scheme's single namespace approach.

Even ignoring ES4's type system, ES4 adds all the following abstraction mechanisms to those in ES3:

  • classes
  • packages
  • units
  • namespaces

I have not heard anyone explain any of these as syntactic sugar that could be expanded away into remaining language elements, so I fear all of these would be fundamental. Worse, several of these constructs can only appear at top level, and so destroy the almost perfect lexical nestability enjoyed by ES3.

If instead classes, for example, were defined purely by syntactic expansion to the constructs in ES3.1, then classes would inherit the lexical nestability of the constructs they expand into. Even Java's classes are lexically nestable!

The namespace mechanism seems motivated by a failure to appreciate the power available by modest extensions of simple lambda abstraction. ES3 already provides some of the features for supporting such modest extensions: Several libraries (such as YUI) already use naming paths such as foo.bar.baz. This is supported by ES3's lexical scoping combined with its extensible records and its property lookup notation. Why should we prefer foo::bar::baz? ES3 already suffers from having two bottom values -- null and undefined -- with no general consensus of when one should use which. How are we to regard mixed naming paths such as foo.bar::baz? When should one use a record as a naming container vs a namespace? What is gained by having two mechanisms?

I could go on and on about ES4's types gone wild, and I will if there's interest. But for now I think one example will suffice:

When I first read the overview, I was puzzled by the lack of uniformity between the types [int] and [int, String]. IIUC, the first represents the type of all arrays all of whose elements are ints. The second represents all 2+ length arrays whose first two elements are an int and a String. I was puzzled: How does one represent the type of all arrays whose first element is an int? I now understand that this is considered an anti-pattern that the language purposely makes inexpressible. I have read but do not understand why this is considered an anti-pattern. No matter. Let's suppose it is an anti-pattern. I would agree that one job of a language design is to gently lead programmers away from bad patterns and toward good ones.

However, this particular syntactic choice is a programmer-hostile way to go about it. It is a trap for the unwary, who will be repeatedly burned by misreading [int]. If I were to agree about what abstract types should be and not be expressible, I would choose surface syntaxes that would make their differences obvious. Perhaps something like [int*] and [int, String, *]. Then, if a naive programmer did wish to express the mysteriously verboten concept of, for example, the type of arrays representing a pair of an int and a String, they might foolishly write [int, String] and the compiler could catch their silly mistake, giving them an error message that would enlighten them about why this concept should be inexpressible.

Btw, call me foolish, but why should the concepts of "array of exactly one int" and "array of exactly one int followed by one String" be inexpressible? I have read a justification of the first, but I failed to understand it.

# Maciej Stachowiak (18 years ago)

On Mar 10, 2008, at 9:54 PM, Mark Miller wrote:

ES3 has several abstraction mechanisms:

  • lambda abstraction, which it gets approximately as right as Scheme!
  • objects as a generalization of records, which has some pros and cons
  • prototype-based sharing of common behavior, which is used almost exclusively by JavaScript programmers to express only class-like patterns.

Altogether, ES3 has many virtues and many problems. One of its great virtues is its almost perfect support for lexical nesting. Virtually any thisless construct that could appear at top level can also appear within a nested lexical context with the same meaning. ES3 also avoids the CommonLisp trap of multiple namespaces, instead siding with Scheme's single namespace approach.

Even ignoring ES4's type system, ES4 adds all the following abstraction mechanisms to those in ES3:

  • classes
  • packages
  • units
  • namespaces

You forgot interfaces (and the type system also adds record types,
(sort-of)-tuples, typed arrays and parametric types). That does seem
like a lot.

# Geoffrey Garen (18 years ago)

ES3 has several abstraction mechanisms:

  • lambda abstraction, which it gets approximately as right as Scheme!
  • objects as a generalization of records, which has some pros and
    cons
  • prototype-based sharing of common behavior, which is used almost exclusively by JavaScript programmers to express only class-like patterns.

Altogether, ES3 has many virtues and many problems. One of its great virtues is its almost perfect support for lexical nesting. Virtually any thisless construct that could appear at top level can also appear within a nested lexical context with the same meaning. ES3 also
avoids the CommonLisp trap of multiple namespaces, instead siding with Scheme's single namespace approach.

Even ignoring ES4's type system, ES4 adds all the following abstraction mechanisms to those in ES3:

  • classes
  • packages
  • units
  • namespaces

You forgot interfaces (and the type system also adds record types, (sort-of)-tuples, typed arrays and parametric types). That does seem like a lot.

There are also like / wrap types.

Geoff

# Dave Herman (18 years ago)

I agree with a number of your objections. I hope you don't mind if I try to distinguish between some of the points I agree with and disagree with.

The current fad in language design is expressive static type systems. As a result, several recent type systems have grown eat their language. Java 1.5 is a particularly nasty example. Simple bits of Java code become so overwhelmed by complex type declarations as to obscure the logic of the code itself. Proposed ES4 suffers from the same disease, though it has not (yet) come down with as bad a case as Java.

I'd love for us to get past the points about popularity and fads. I know that static types are all the rage and that dynamic languages have consequently suffered in the court of popular opinion. I'm a member of the Scheme community so I feel it acutely.

But I'm skeptical of your comparison to Java 5. Java is a statically typed language that gained even more types. The ES4 proposal is a dynamic language with optional type annotations. These are there to help people when they feel that types would serve to document their code and type checking would help improve the robustness of their code and/or make the kinds of ad-hoc checking (hand-written assertions) cleaner. When people don't feel they add value, they don't have to use them.

In Java 5, you're locked in to complex type annotations and can't get out. An important design goal in the ES4 type system has been to make the relationship between typed and untyped code relaxed enough that this doesn't happen.

Even ignoring ES4's type system, ES4 adds all the following abstraction mechanisms to those in ES3:

  • classes
  • packages
  • units
  • namespaces

I'm sympathetic-- I've always been uncomfortable with packages and namespaces. (My reaction to units was more hopeful, since the global object is so problematic in ES3, but I'm not yet up to speed on the proposal.)

If instead classes, for example, were defined purely by syntactic expansion to the constructs in ES3.1, then classes would inherit the lexical nestability of the constructs they expand into. Even Java's classes are lexically nestable!

Nestability is a good design goal, and I'm glad you brought it up.

Syntactic expansion has many benefits, too, of course. I just want to point out the main pitfall in making classes syntactic sugar. One of the real problems with ES3 is the inability to make guarantees about any bindings except for lexical ones. With vanilla ES3 objects, there's no way to share information between (conceptual) modules of code and preserve the integrity of the container, unless you either a) hide the data in a closure or b) copy the data into a dummy container.

Classes, in addition to being a well-understood mechanism and the de facto style of the JS community, should help the programmer protect the integrity of their bindings. For example, a class declaration in ES4 creates "fixtures" for its properties-- meaning that the programmer can rely on them not being deleted. The current ad hoc approaches in use in ES3 can't guarantee that, and manually using closures is too heavyweight.

That said, you've hinted at alternative approaches, perhaps with constructs desugaring to closures of some sort:

The namespace mechanism seems motivated by a failure to appreciate the power available by modest extensions of simple lambda abstraction.

I'd certainly like to hear what you're thinking here.

Why should we prefer foo::bar::baz? What is gained by having two mechanisms?

Fair criticisms (and I personally feel the namespace mechanism is one of the most deserving of criticism). Primarily, the purpose it's serving is a way to protect object properties with a "key" that you can then control via e.g. lexical hiding. The Foo.Bar.baz pattern in ES3 doesn't provide a way to hide such bindings.

When I first read the overview, I was puzzled by the lack of uniformity between the types [int] and [int, String]. IIUC, the first represents the type of all arrays all of whose elements are ints.

That's not my understanding. IIRC, for a type [T1,...,Tn,Tn+1] there are required to be zero or more array elements of Tn+1. So [int] is the type of arrays with zero or more ints, and [int, String] is the type of arrays with an int at index 0 followed by zero or more Strings.

I'll grant that this is unconventional. But it's an attempt to make the type structure match actual usage in JS: people use arrays both as arrays and as tuples. So we have a type that accomodates the idiomatic usage of the language.