Outline of exactly how @@create works?

# Domenic Denicola (12 years ago)

(Forked from a public-script-coord thread; thought it would be useful to es-discuss as well.)

From: Allen Wirfs-Brock [allen at wirfs-brock.com]

On Aug 7, 2013, at 12:33 PM, Boris Zbarsky wrote:

Just to make sure I understand the behavior of @@create: it can be invoked to create new objects, but cannot be invoked on an existing object, right?

Right, @@create just creates a new, uninitialized instance of a specific kind of object (could be an exotic object with strange internal representation). It is the responsibility of operations upon such objects (including the constructors that initialize such instances) to recognize them and whether or not they have been properly initialized.

Allen, would you have time to write up exactly how @@create works for all of us? I think I understand it, based on piecing together a lot of threads since its inception, but since it will be a very important part of designing solid primitives for the web platform, a more complete outline would be helpful. For example, here are the questions that come to mind for me. I am sure the answers to them overlap substantially.

  • What is the difference between @@create and the constructor? Which responsibilities belong to each?
  • How exactly does @@create play a role in subclassing and enabling better subclass support?
  • What role does @@create play in creating well-behaved classes, generally?
  • When is @@create necessary at all? When writing our own classes, or designing classes for the platform, when can we leave it out?
  • How will classes that use @@create differ from those that don't?
  • What problem exactly does it solve that ES5's non-stratified construction falls down for?
  • To what extent is it used in the ES6 built-ins, and for what purpose? Has it been "retrofitted" onto any older built-ins? If so, how does that change their behavior from how they were specced in ES5? If not, what differences does this cause between the older built-ins and newer built-ins?
  • Is there any mechanism in the spec, current or planned, for extracting a class's @@create value? If I got ahold of one, what could I do with it? E.g. Map[@@create](somethingThatsNotAMap).
# Allen Wirfs-Brock (12 years ago)

On Aug 7, 2013, at 12:52 PM, Domenic Denicola wrote:

Allen, would you have time to write up exactly how @@create works for all of us? I think I understand it, based on piecing together a lot of threads since its inception, but since it will be a very important part of designing solid primitives for the web platform, a more complete outline would be helpful. For example, here are the questions that come to mind for me. I am sure the answers to them overlap substantially.

You should probably start with meetings:subclassing_builtins.pdf In particular the first half of that deck.

  • What is the difference between @@create and the constructor? Which responsibilities belong to each?
  • How exactly does @@create play a role in subclassing and enabling better subclass support?
  • What role does @@create play in creating well-behaved classes, generally?
  • When is @@create necessary at all? When writing our own classes, or designing classes for the platform, when can we leave it out?
  • How will classes that use @@create differ from those that don't?
  • What problem exactly does it solve that ES5's non-stratified construction falls down for?
  • To what extent is it used in the ES6 built-ins, and for what purpose? Has it been "retrofitted" onto any older built-ins? If so, how does that change their behavior from how they were specced in ES5? If not, what differences does this cause between the older built-ins and newer built-ins?
  • Is there any mechanism in the spec, current or planned, for extracting a class's @@create value? If I got ahold of one, what could I do with it? E.g. Map[@@create](somethingThatsNotAMap).

Which of these questions are still unanswered by the above doc?

# Domenic Denicola (12 years ago)

From: Allen Wirfs-Brock [allen at wirfs-brock.com]

Which of these questions are still unanswered by the above doc?

Wow, it was a good document; I'm sad I forgot about it.

I think what's still missing would be some guidance, on when it's needed in designing platform classes or user classes, and why it was used for the new built-ins. (I think this ties in to your caution over in public-script-coord to not use that pattern much.) Also, what would the practical consequences of moving everything into @@create be, ignoring the constructor entirely?

One point I was confused on, that re-reading helped me understand, is that @@create actually creates the object instances; it does not manipulate passed-in instances. Thus, getting ahold of e.g. Date[@@create] or HTMLElement[@@create] would not allow you to do anything special; you could not e.g. turn a normal object into a Date. The most you could do is create an "uninitialized" object, and what that means exactly depends it seems on how much stuff you've shifted into @@create vs. the constructor.

# Allen Wirfs-Brock (12 years ago)

On Aug 7, 2013, at 1:22 PM, Domenic Denicola wrote:

I think what's still missing would be some guidance, on when it's needed in designing platform classes or user classes,

First, this is the default way that the new operator works in ES6. A @@create method is always involved. Your real question is: When do I need to over-ride the default implementation of @@create that functions inherit from Function.prototype

The answer is: Anytime you want newing a constructor function to return an exotic object, or if you need to brand its instances, associate private state, or otherwise manipulate the instance before initialization them.

A constructor that returns a proxy is probably the most common example of the exotic object use case:

class MyExotic {
   static [@@create] () {return new Proxy({  }, myHandler)}
   constructor (...args) {
         //initialize this value using args
    }
}

let branded = new WeakSet;
class BrandedMyMe {
   static [@@create] () {
        let obj = super();  //call the default @@create
        branded.add(obj);
        return obj
    }
   constructor (...args) {
         if (!branded.has(this)) throw new TypeError();
         //initialize this value using args
    }
}

class FixedFields {
   static [@@create] () {
        let obj = super();  //call the default @@create
        Object.defineProperty(obj, 'x', {value: 0, writable: true, configurable: false});
        Object.defineProperty(obj, 'y', {value: 0, writable: true, configurable: false});
        return obj
    }
   constructor (x,y) {
         this.x = x;
         this.y = y;
    }
}

and why it was used for the new built-ins. (I think this ties in to your caution over in public-script-coord to not use that pattern much.)

It's use by built-ins so that the creation of appropriate kinds of exotic objects, branding, and inclusion of private state slots may be inherited by subclasses while still allowing the actual constructor initialization to be over-ridden by the subclass.

The caution is really about avoiding excessive or unnecessary branding.

Also, what would the practical consequences of moving everything into @@create be, ignoring the constructor entirely?

constructor arguments are not passed to @@create. The role of @@create is to physically allocate the right kind of object based upon the internal design of the class. The role of the constructor is to initialize the newly allocated instances based upon the argument pattern passed to the constructor. When subclassing you are likely to need to independently inherit or over-ride each capability.

One point I was confused on, that re-reading helped me understand, is that @@create actually creates the object instances; it does not manipulate passed-in instances. Thus, getting ahold of e.g. Date[@@create] or HTMLElement[@@create] would not allow you to do anything special; you could not e.g. turn a normal object into a Date. The most you could do is create an "uninitialized" object, and what that means exactly depends it seems on how much stuff you've shifted into @@create vs. the constructor.

exactly, the rough equivalence to remember is:

new Foo(...args)

does the same thing as :

Foo.apply(Foo[@@create](), args)
# Allen Wirfs-Brock (12 years ago)

On Aug 7, 2013, at 2:41 PM, Allen Wirfs-Brock wrote:

A constructor that returns a proxy is probably the most common example of the exotic object use case:

class MyExotic {
  static [@@create] () {return new Proxy({  }, myHandler)}
  constructor (...args) {
        //initialize this value using args
   }
}

actually the @@create should mroe likely bebe

static [@@create]() {
  return new Proxy(Object.create(this.prototype), myHandler)
}

One of the responsibilities of a @@create method is to setup the prototype inheritance chain of the new object.