ECMA TC39 meetings 14th October 1999

These notes cover the 3 meetings that took place consecutively at HP Labs, Bristol:

Present:

Dave
Waldemar
Richard
Chris
Herman
Peter
Mike M
Clayton (some of the time)

Agenda

What shall we discuss today? What do we need to have done for Seattle?

Chris asks medium will be used to create the spec. Herman suggests that ought to be discussed in the wider forum, not the Modularity subgroup.

The issues of the standard - both presentation and formalism should be discussed in the next working group meeting (Seattle, November).

Review of the Bristol Agreements

Waldemar thinks we agreed to "function" for class methods rather than "static method" as recorded in Dave's meeting notes.

Waldemar would prefer to avoid a distinction between global functions and class methods. For this reason, he advocates the use of "function".

Herman prefers to distinguish legacy function from static methods which would be required to be handled strictly. Herman reports resistance at Microsoft for there being any difference between functions and methods. Basically having both functions and methods is very confusing and inappropriate for a scripting language.

Waldemar notes that he met with a firestorm from his colleagues for the colon syntax for types.

Richard thinks it is clear that we want to distinguish between instance methods and other functions, but less clear that we need to distinguish global from class functions.

Accessing class variables

Herman proposes that there is an implicit "this" in methods for field references. He says that Microsoft have some scenarios where this is important for them.

Access to static class variables from "this"? Java supports this. Mike suggests that this feature shouldn't be permitted in ECMAScript. Herman puts it: do we want to go the Java way or do we want to be more rigorous? Waldemar writes:

class C
{
    public:
        static int x;
}

o = new C;

o.x;  // is this allowed?

Chris asks should unadorned identifiers in instance methods access instance variables? If no, then you are required to write this.y to reference instance variable y within an instance method.

var y;

class C extends B
{
    static field x ...
    field y;

    public method foo()
    {
        ... x...        // class var x
        ... y ...       // instance var or global var
        this.y ...      // instance var y
        window.y ...    // browser global var y
    }

    public static method bar()
    {
        ... y ...        // what does this mean?
    }
}

The unadorned y is a reference to the field y. An open question is what would y mean in a class method? Is it illegal or does it refer to the instance field y, i.e. there is an implicit this.

Chris wonders if an implicit keyword for a field could be required for unadorned references to bind to field names.

After further discussion we agree to:

Unadorned references to fields of the class and superclass that a method appears in are taken to refer to fields of the current instance (i.e. x is taken to be this.x). In class methods it is an error to make an unadorned reference to an instance field.

Function vs Static Method

We return to the question raised earlier on. We note that ECMAScript has several different meanings for function and we want to avoid further overloading the term.

Herman proposes we make it illegal for a function to appear within a class. We agree to hold off on what it means for a function to occur within a class. For now, by treating this as an error, Herman is playing it safe. Outside of a class, function means the same as in edition 3.

We confirm our agreement to "static method".

Instance and Class fields

We confirm our agreement to "field" for instance variables and "static field" for class variables. In future, we may choose to make var a synonym for field or static field.

Nested classes

Chris proposes we hold off on nested classes, so that for now implementations should treat nested classes as illegal. We discuss Java's treatment of nested classes. In Java they are useful, in part because Java doesn't have closures. Waldemar is concerned about namespace management implications of nested classes. For instance is it reasonable for inherited fields to take precedence to peer fields for nested classes.

We discuss this, and agree in principle to having nested classes, provided we can clarify the details.

Action: Waldemar to draft a proposal for nested classes for discussion in the November meeting. Waldemar notes this will be derived from the treatment in C++.

Action: Waldemar to add Peter (ptorr@microsoft.com) to the eswg mailing list

Action: Waldemar to provide web access to an archive of eswg email.

Action: Chris to move the old modularity mailing list discussion archive to a safe place.

Lunch break

We choose to talk around a number of topics with the aim of surfacing issues for later resolution.

Method override

class A
{
    public method m();
}

class B extends A
{
    public new method m();
}

Waldemar initiates a discussion on method overrides. Do we want a keyword (such as "new") to indicate when a method is to act as an override? Waldemar notes the idea is not to change semantics, but rather to catch errors.

Herman asks are we going to support both virtual and non-virtual methods. The general feeling is to make all methods virtual. If we add "final", then that would provide one way to prevent overrides.

Peter notes that simple things should be simple. You shouldn't need to write the override keyword on simple classes. Is override the default?

We have a discussion of whether instances of classes permit ad hoc (expando) properties. Will this cause problems? Waldemar thinks that namespace problems could arise. Is it sufficient for the run-time to switch to a less efficient treatment when it detects that an ad hoc property has been added to a class?

Dave asks about adding "final" to methods. The response is that override is not use to make a method final. Instead it's used to make a fresh start.

We talk about the possibility of other terms than "override". Herman is happy with "override", Chris and Waldemar kind of like "extend". Herman agrees under duress to "flying the override flag" and seeing what reaction we get. We will flag an warning if you use override and you are not overriding, and vice versa. The use of the override keyword doesn't alter the semantics, but is useful as a means for detecting problems.

Inheriting classes across packages?

Waldemar asks if we can preclude this? Can we limit inheritance to just within a package for selected classes, asks Richard.

This raises the question of using scripts to create derived types from foreign objects, for instance Java, and how you access ECMAScript objects from other languages. This seems like a desirable feature with obvious applications.

Chris adds it would be nice to be able state that a class can't be extended. This could apply to classes whether written in Java or ECMAScript. You might want to allow subclasses within a package but not outside. Clayton suggests that the "final" keyword could work this way. He also asks about "sealed" which would fix the class so as to preclude ad hoc properties. Waldemar prefers to require "unsealed" to be used in you want to allow ad hoc properties.

Execution Model

The implications of the streaming model.

Herman writes:

function foo(x)
{
    class C extends x
    {
    }
}

Herman asks when do we compile C? One possibility is to disallow classes within functions. Herman then asks what about:

for (x in Foo)
{
    class C extends x
    {
    }
}

Waldemar suggests this could be handled by shoving the class declaration inside an eval statement and making sure that classes work within eval. He doesn't want to introduce a distinction between compile-time and run-time.

For the lightweight profile of ECMAScript for wireless applications, we could place strong constraints on type expressions. We may even preclude eval for this profile.

Execution model and block scoping

Waldemar writes:

function f()
{
    var a;
    const t = f_with_side_effects();

    c = "abc";

    {
        var b;
        var c:t;
        local var d;
    }
}

Waldemar proposes that the "local" keyword means that the variable is scoped to the block in which it is defined.

When you evaluate c="abc" you check the type of c. The var c:t declaration sets the type of c to undefined since t won't be evaluated until run-time.

We like the idea that it's an error to assign to a variable before that variable is declared, when the declaration specifies a type.

Richard doesn't agree. He would prefer it to be ok when the declared type can be shown to match the assignment.

Principles: a) typed variable declarations must precede use of such variables b) adding type to variable declarations shouldn't alter the behavior of the program.

foo(x);   // call foo

// declare foo
function foo(x:string):int
{
}

Herman says that if we treat functions and variables consistently, then we can't apply the proposed rule for typed variables having to occur before the variable is used.

function f1()
{
    f2();
}

function f2()
{
    f1();
}

f1();

We agree that the above example would work.

Waldemar writes the following example:

var a;  // allowed for compatibility with
var a;  // Edition 3 (warning in strict mode)

var b:int;  // not allowed
var b:int;

var c:string;  // not allowed
var c:int;

What about declarations in for loops

for (var i = 0; i<10; i++)
{
    // d has different type on each iteration
    local var d:array[i];
    var e:array[i];
    var g:int;
}

One model has it that variables declared within for loops only exist for a single iteration of the loop and are freshly created on the next iteration.

It would be nice to make local the default, but this would break existing programs. One option would be to add a local keyword before the opening brace for the block. However we don't like being forced to use a local keyword.

Richard proposes that successive type declations for the same variable reset the type, e.g.

var c:string;

c = "abc";

var c:int;

c = 23;

In summary, we are leaning towards the interpretation that var a:t sets the value of the variable to the default value for that type. It is an error to use a variable before its type declaration.

Herman notes that it's ok for an old program to produce an error in Edition 4, but it would be terrible if its behavior to be silently changed between Edition 3 and 4.

Herman would like Peter's example to be an error. Peter wrote:

var x:int;
x = 42;
var x:int;  // resets x to default value for int
print(x);

We would like this to still be an error if you drop the type annotation to the 2nd var statement. Herman is still undecided about:

x = 42;
var x:int;
print(x);

Most of us want this to be an error.

Peter describes a use case where several different authors provide ASP scripts for use in the same HTML file. It's convenient for each of the pieces to declare the variables they use. If they share the same variable this makes it natural to want to allow multiple declarations of the same variable. Provided these are all consistent, then Peter wants this to work.

Waldemar summarises the alternatives:

  1. When encountering a type declaration, redefine the type of the variable and either convert, or set to default value, or require an initializer.

  2. Allow typed redeclarations with an identical type (how do you determine this?)

    Waldemar notes that this would preclude the ability to conditionally declare variables and later use them in subsequent conditional sections, e.g.

    if (env == "Mac")
        var color:string = "purple";
    
    ....
    if (env == "Mac")
        paintWidget(color);
    

    Herman doesn't like this option as we can't change untyped variables to be block-local since this would break existing programs.

  3. Make typed declarations block-local

  4. Require type expressions to be compile-time expressions (contrary to dynamic language)

Clayton is sympathetic to the dissonance argument against (C). One candidate would be (B) with "local" or "my" as a shorter version of "local var", where the default is not local.

We agree to (B) but only from the same place in the source code. By identical type, at least for objects, we mean the same pointer. We agree (at least for now) to support a mechanism for block-local variable scoping provided it is not the default.

Both Waldemar and Herman will look at the semantics of types in preparation for November in Seattle. Herman is interested in using a different notation for describing types in the specification. Herman wants to talk about try-catch in November.

End of meeting at 5:12pm.