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

Mark S. Miller erights at google.com
Mon Mar 24 18:45:49 PDT 2008


On Sun, Mar 23, 2008 at 11:28 PM, Brendan Eich <brendan at mozilla.org> wrote:
>  ES4 is not statically typed, so...
>  ... this is a false dilemma.
>
>  These analogies are weak and tendentious in my opinion. Let's try to
>  get back to premises and argue forward. Can we start with why you
>  seem to believe that ES4 is statically typed?

The language design I accused of being statically typed is the
language design currently called "proposed ES4". Of course, one of the
things at stake is whether ES4 will be statically typed. As I've made
clear, I hope not.

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. It pays most of
the costs of static typing but obtains few of the benefits. And it
inherits all the costs of having been dynamically typed, but retains
few of the benefits.

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)

Costs of Static Typing

* language complexity
* limit expressiveness to statically checkable type "predicates".
:int, but no :prime
* two "expression" languages - static type expressions vs dynamic
value expressions
* multiple partial type theories: nominal, structural, duck, ...
* verbosity (inside Google, we've expanded to a 100 column limit for
Java generics)
* inappropriate for casual scripting audience

Benefits of Dynamic Typing

* lambda abstraction / objects is all you need
* Tennent correspondence
<http://gafter.blogspot.com/2006/08/tennents-correspondence-principle-and.html>
* all abstractions first-class, composable
* simple meta-interpreters can enable powerful meta-programming
* syntactic simplicity supports other metatools: minifiers, lints, ...
* rapid prototyping

Costs of Dynamic Typing

* runtime space and time costs
* less static knowledge of dynamic behavior

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.

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

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


A comprehensive treatment of all these points would need a book. Here,
I will illustrate with a small example as my belated response to Dave
Herman.

I wrote:
>  > 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.

On Tue, Mar 11, 2008 at 5:49 AM, Dave Herman <dherman at ccs.neu.edu> wrote:
>  Nestability is a good design goal, and I'm glad you brought it up.
>  [...]
>  That said, you've hinted at alternative approaches, perhaps with
>  constructs desugaring to closures of some sort

To get discussion going, I will here present a first cut at a
desugaring of something like proposed ES4's class syntax and type
declarations into ES3.1 constructs. Were ES4 to be redefined in terms
of such syntactic sugar, then the desugared semantics could be
precisely ES3.1's would thereby preserve most of ES3.1's virtues. The
example below demonstrates the value of preserving ES3.1's lexical
nestability and first-classness. Browsers would only need to initially
implement ES3.1, and the expansion of ES4 to ES3.1 could happen
initially offline or on the server.


                                                         Classes as Sugar

Given something like the __createProperty__ operation we've been
discussing lately, and that Lars has made progress on specifying, we
could imagine generalizing it to also constrain properties to be
non-overridable (as in proposed ES4 "final") and protected (addessable
only by way of "this."). We could also imagine an operation
constraining an object to be non-extensible (i.e., "fixture"). For
purposes of this note, I will make up a plausible static API for
these. Were the proposed ES4 class syntax defined in terms of the
obvious expansion to ES3.1 + calls to these functions, the first class
definition in the proposed ES4 overview doc

  class C {
    var val;
    var large = Infinity;
    const x = 3.14;
    function f(n) { return n+val*2; }
  }

would expand to

  function C() {
    Object.__createProperty__(this, 'val', undefined, Readable | Settable);
    Object.__createProperty__(this, 'large', Infinity, Readable | Settable);
    Object.__createProperty__(this, 'x', 3.14, Readable);
    Object.fix(this);
  }
  Object.__createProperty__(C, 'prototype', C.prototype, Readable);
  Object.__createProperty__(C.prototype,
                                                    'f',
                                                    function(n)
{return n+val*2;},
                                                    Readable);
  Object.fix(C.prototype);
  Object.fix(C);

ES3 constructor functions are already used as, in effect, nominal type
identities by ES3 instanceof expressions. By expanding the class
syntax to operations that make C's 'prototype' property read-only, 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(...);
    }

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. These
would translate directly into the parameters of the constructor
function that the class expands to.

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.
Translating Ping's example from
<http://www.erights.org/elang/kernel/auditors/> into an ES4 using this
sugar:

function makeBrand() {
    var key = {};
    class Envelope(payload) {
        protected const contents = payload;
        function open(k) {
            if (k === key) { return this.contents; }
        }
    }
    return {
        sealer: function(payload) { return new Envelope(payload); },
        unsealer: function(env :Envelope) { return env.open(key); }
    };
}

The key to the correctness of the above code is that every call to
makeBrand create a distinct nominal Envelope type, so that the
unsealer of a sealer/unsealer pair can use it to check that the
alleged envelope argument is not only an instance of the static
Envelope code, but is an envelope created by a call to the
corresponding sealer.


-- 
 Cheers,
 --MarkM


More information about the Es4-discuss mailing list