Proposal: Improve syntax for inline anonymous class instantiations

T.J. Crowder tj.crowder at farsightsoftware.com
Sat Jan 7 10:07:04 UTC 2017


Two aspects to this: Motivations and syntax.

## On motivations:

Addressing new syntax, the first question has to be: Is this use case
sufficiently common and painful that it needs new syntax? The answer
may be yes, but we need to ask the question.

Trying to solve it without new syntax with a helper function, I've
come up with three ways of getting those arguments up front. I'm not
all that happy with any of them.

### Helper #1:

This kind of has the opposite problem (the class is hidden away at the
end), but it's dead simple:

```js
this.add(makeInstance("items", itemsModel, class extends ArrayView {
    populateItem(item) {
        item.add(new Checkbox("check", new PropertyModel(item.model, "done")));
        item.add(new Label("title", new PropertyModel(item.model, "title")));
    }
}));
```

...where `makeInstance` is:


```js
const makeInstance = (...args) => {
    return new args[args.length - 1](...args.slice(0, args.length - 1));
};
```

### Helper #2:

Puts things in the desired order, but isn't exactly elegant at point-of-use:

```js
this.add(makeInstance(ArrayView, "items", itemsModel, parent => class
extends parent {
    populateItem(item) {
        item.add(new Checkbox("check", new PropertyModel(item.model, "done")));
        item.add(new Label("title", new PropertyModel(item.model, "title")));
    }
}));
```

...where `makeInstance` is:


```js
const makeInstance = (parent, ...rest) => {
    const args = rest.slice(0, rest.length - 1);
    const subclass = rest[rest.length - 1](parent);
    return new subclass(...args);
};
```

### Helper #3:

Gets really close in terms of usage, but because the methods would
have the wrong [[HomeObject]] (Igor's example didn't use `super` in
`populateItem` but presumably it could have), it has to resort to not
one but two `setPrototypeOf` calls, which is just ugly:

```js
this.add(makeInstance(ArrayView, "items", itemsModel, {
    populateItem(item) {
        item.add(new Checkbox("check", new PropertyModel(item.model, "done")));
        item.add(new Label("title", new PropertyModel(item.model, "title")));
    }
}));
```

...where `makeInstance` is:

```js
const makeInstance = (cls, ...rest) => {
    const args = rest.slice(0, rest.length - 1);
    const methods = rest[rest.length - 1];
    const subclass = class extends cls { };
    Object.setPrototypeOf(methods, cls.prototype);
    Object.setPrototypeOf(subclass.prototype, methods);
    return new subclass(...args);
};
```

(Hey, I warned you it was ugly.) (This isn't the first time I've
wanted to change a method's [[HomeObject]] after creation.)

Maybe someone else can do better.

Or, of course, just don't use an anonymous class.

## On syntax:

Looking at:

```js
this.add(new ArrayView("items", itemsModel) {
    populateItem(item) {
        item.add(new Checkbox("check", new PropertyModel(item.model, "done")));
        item.add(new Label("title", new PropertyModel(item.model, "title")));
    }
});
```

A couple of notes/observations:

* This is exactly how Java handles [doing the same
thing](http://docs.oracle.com/javase/tutorial/java/javaOO/anonymousclasses.html).
JavaScript is not Java, of course, but worth noting prior art.

* It would be good to hear early from implementers if this presents a
parsing challenge. Until the `{`, it looks like you're instantiating
`ArrayView` with `"items"` and `itemsModel`; the `{` then changes
things. I don't know if that's backtracking, or looking ahead, or
what. Obviously Java parsers handle it, but again, different language,
different challenges.

* If you remove the `this.add(...)` part of that, there's an ASI
hazard if you put a line break before the `{` -- it becomes `new
ArrayView("items", itemsModel);` followed by a block (which then has
invalid content). (Not an insurmountable problem, it's not like it's
the only ASI hazard caused by a line break before a `{`. But it would
add another one.)

-- T.J.
Farsight Software Ltd | 20-22 Wenlock Road, London N1 7GU | Company #8393428

tj.crowder at farsightsoftware.com | Direct: +44 (0)20 3627 4231 |
Mobile: +44 (0)7717 842 414



If you've received this message in error, please let us know by
forwarding it to info at farsightsoftware.com and then delete it from
your system. Please don't copy it or disclose its contents to anyone.
Separately, note that email sent over the internet without a digital
signature may be modified en route.


On Sat, Jan 7, 2017 at 5:35 AM, Igor Vaynberg <igor.vaynberg at gmail.com> wrote:
>
>> On Jan 6, 2017, at 7:04 PM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:
>>
>> (new class extends foo(bar) {…})
>> is already valid syntax that means use the value return from calling foo with argument bar as the superclass of the class that is being instantiated. What you propose would be a breaking change.
>
> What about limiting it just to (new foo(bar) {...}) syntax?
>
> -igor
>
> _______________________________________________
> es-discuss mailing list
> es-discuss at mozilla.org
> https://mail.mozilla.org/listinfo/es-discuss


More information about the es-discuss mailing list