tc39 / ecma262

Status, process, and documents for ECMA-262
https://tc39.es/ecma262/
Other
15.04k stars 1.28k forks source link

Define exotic object from first principles #1453

Closed annevk closed 4 years ago

annevk commented 5 years ago

From @jmdyck in https://github.com/heycam/webidl/issues/655#issuecomment-467472205:

I think the reason that the ES spec says: Let _A_ be a newly created Array exotic object. rather than: Let _A_ be ObjectCreate(...). is that the latter form doesn't give you anything explicit that says "this is an Array exotic object", which you need, for example, when an algorithm step says: If _argument_ is an Array exotic object, ...

It seems to me that if "exotic object" was instead defined as an object with an overridden internal method, this could all be simplified somewhat. Incidentally, this would also make it easier to define the Location object through a mix of IDL and overridden internal methods.

devsnek commented 5 years ago

The algorithm of ObjectCreate can't produce an exotic object. It always produces a "newly created object", the same way creating an array happens via producing a "newly created Array exotic object".

domenic commented 5 years ago

The question is, can you turn the result into an exotic object? E.g. is this legal?

  1. Let X be ObjectCreate(...)
  2. Set X's [[DefineOwnPropertyMethod]] to the one defined in section Y.

Would the result then be that X is an exotic object? Or, did we do something illegal in step 2?

The same question for if we replace step 1 by "Let X be a newly created object".

devsnek commented 5 years ago

@domenic regardless of if its allowed, i can imagine people trying to implement the spec would be annoyed, because it would become much less explicit when you were allocating a specific exotic type.

zenparsing commented 5 years ago

The current spec is not consistent here:

https://tc39.github.io/ecma262/#sec-modulenamespacecreate

I agree that the current editorial convention of creating an exotic object by named type and then imperatively overwriting the essential methods is a bit confusing. On the other hand, I think framing all objects as being "cut from the same cloth" might be equally confusing (as @devsnek is pointing out).

I wonder if it's possible to go the other direction and make things less imperative.

Take ArrayCreate, for example:

Does it help to have the second and third steps at all? Or could we just have the first step, with a link to the definition of those exotic objects?

domenic commented 5 years ago

That direction seems somewhat promising, but at its core it seems to just push the problem back into the definitio nof "a newly created Array exotic object". How would you define that?

allenwb commented 5 years ago

I thought I had this response https://github.com/heycam/webidl/issues/655#issuecomment-467559106 here but it end up on the WebIDL thread.

jmdyck commented 5 years ago

The question is, can you turn the result into an exotic object? E.g. is this legal?

  1. Let X be ObjectCreate(...)
  2. Set X's [[DefineOwnPropertyMethod]] to the one defined in section Y.

Would the result then be that X is an exotic object? Or, did we do something illegal in step 2?

I'm pretty sure there's nothing in the spec that would make step 2 illegal.

The same question for if we replace step 1 by "Let X be a newly created object".

It's legal (and is used in the spec), but you'd need to also say how the other essential internal methods are set. (See BoundFunctionCreate, IntegerIndexedObjectCreate, ModuleNamespaceCreate, and ProxyCreate.)

jmdyck commented 5 years ago

@devsnek:

regardless of if its allowed, i can imagine people trying to implement the spec would be annoyed, because it would become much less explicit when you were allocating a specific exotic type.

The pseudocode would still invoke ArrayCreate or IntegerIndexedObjectCreate etc. Isn't that explicit enough?

jmdyck commented 5 years ago

That direction seems somewhat promising, but at its core it seems to just push the problem back into the definition of "a newly created Array exotic object". How would you define that?

9.4.2 Array Exotic Objects has the paragraph:

Array exotic objects provide an alternative definition for the [[DefineOwnProperty]] internal method. Except for that internal method, Array exotic objects provide all of the other essential internal methods as specified in 9.1.

This is almost specific enough to indicate what a newly created Array exotic object means, and also what _X_ is an Array exotic object means. (Personally, I think I prefer a more explicit, imperative approach.)

jmdyck commented 5 years ago

@annevk:

It seems to me that if "exotic object" was instead defined as an object with an overridden internal method, this could all be simplified somewhat.

That's pretty much how it is defined: an object with non-default behavior for any of its essential internal methods. Is the distinction between "overridden" and "not the default" important to the simplification?

allenwb commented 5 years ago

Would the result then be that X is an exotic object? Or, did we do something illegal in step 2?

I'm pretty sure there's nothing in the spec that would make step 2 illegal.

My intent in the ES6 revisions was that this formulation would only happen in xxxCreate abstraction operations. Part of the point of create clause 9 was regularize all the adhoc ways that object creation were described in editions prior to ES6. (But hard to say "illegal" as the meta language of ECMA-262 is all convention and subject to change.

The same question for if we replace step 1 by "Let X be a newly created object".

It's legal (and is used in the spec), but you'd need to also say how the other essential internal methods are set. (See BoundFunctionCreate, IntegerIndexedObjectCreate, ModuleNamespaceCreate, and ProxyCreate.)

The only places I find it used the spec. is within the definitions of those four xxxCreate abstraction operations. It is essentially the incantation used within xxxCreate operations for bring a new object identity into existence. Each of those would be better stated as "a newly created <Bound Function, etc> exotic object" similarly to what is done for the other xxxCreates. The fact that they are written that way was probably just sloppy editing on my part. The "exotic object" formulation implies that the "branding" is tied to the initial identify of a new object and reflects the reality that in some cases implementation may need to allocate different internal data structures for various kinds of exotic objects.

allenwb commented 5 years ago

Things have to bottom out in somewhere.

Perhaps it might be helpful to replace steps like:

  1. Let S be a newly created String exotic object.

with something like:

  1. Let S be the identity of a newly created String exotic object.

Followed by the imperative steps to complete creation of the object. But I actually prefer, the more declarative style of a set of initial state bullet points under the create step.

jmdyck commented 5 years ago

The thing I find odd about the phrase a newly created Array exotic object is that it doesn't really create an Array exotic object. What actually makes it an Array exotic object is the rest of the ArrayCreate operation: setting internal methods + slots and defining a length property. (In fact, a newly created [...] object doesn't even create an object per se, since it doesn't have any of its essential internal methods set, but I'm willing to let that go.)

Perhaps it might be helpful to replace steps like:

  1. Let S be a newly created String exotic object. with something like:
    1. Let S be the identity of a newly created String exotic object.

I don't think inserting "the identity of" would be helpful. (The spec doesn't define "identity", and only uses it once.)

But I actually prefer, the more declarative style of a set of initial state bullet points under the create step.

So something like this?:

    2. Let _A_ be a newly created Array exotic object with the following settings:
      * [[DefineOwnProperty]] as specified in ...
      * other essential internal methods = the default ordinary object definitions.
      * [[Prototype]] = _proto_.
      * [[Extensible]] = *true*.

It's maybe an improvement over the status quo (I see pros and cons), but does it do anything to address @annevk's original concern?

allenwb commented 5 years ago

@jmdyck

...with the following settings:

I wouldn't say "settings". Perhaps "with the following characteristics:"

I've kind of lost what @annevk concerns actually are. ObjectCreate was intend to be used as the primarily way to specify the need to create an ordinary object, not as a short cut for creating exotic objects. I think use it that way would only create more potential for misunderstanding. As I mentioned in https://github.com/heycam/webidl/issues/655#issuecomment-467559106 my intent was that other/future exotic object specifications should follow the general pattern used in 9.4. It isn' t clear to me what issues he does/would have with that pattern particularly if we cleanup the minor irregularities discussed here.

annevk commented 5 years ago

It comes down to https://github.com/tc39/ecma262/issues/1453#issuecomment-467525877. Platform objects are all defined and created in the same way, but some have overridden internal methods. It would be nice if we could tack those on at the end and not have to know about them upfront.

See https://heycam.github.io/webidl/#internally-create-a-new-object-implementing-the-interface and https://github.com/heycam/webidl/issues/660 for the Location exotic object.

We could also add branching, probably even to the extent @jmdyck mentions above, but it's not particularly clear to me the distinction has much meaning as quickly skimming through the creation algorithm of an object will tell you the same thing.

jmdyck commented 5 years ago

ObjectCreate was intend to be used as the primarily way to specify the need to create an ordinary object, not as a short cut for creating exotic objects.

Sure, but if someone wants a shortcut for creating exotic objects, and it comes out looking exactly like ObjectCreate, is there a point to having both?

I think use it that way would only create more potential for misunderstanding.

I think we could lessen any misunderstanding by tweaking the prose for ObjectCreate, making it clear that although the object it returns is ordinary, some callers will then make it exotic by changing one or more of its internal methods.

jmdyck commented 5 years ago

@annevk:

We could also add branching, probably even to the extent @jmdyck mentions above

I didn't mention branching, so I don't know what you're talking about there.

annevk commented 5 years ago

@jmdyck I meant adding branching to the IDL algorithms to enable the kind of prose you proposed. (I.e., instead of overriding some internal methods at the end of creation, branch early on to decide between exotic and ordinary and allocate them in the way you suggest.) I'm not particularly convinced it's worthwhile, but it's all doable.

jmdyck commented 5 years ago

@annevk: You mean the syntax I gave at the bottom of this comment? (Note that I didn't "propose" it, I was just concretizing one of @allenwb's suggestions, for discussion purposes.) Yeah, that syntax wouldn't (easily) allow the abstraction/separation you're asking for, which I consider one of its "cons".

annevk commented 5 years ago

Aye.

allenwb commented 5 years ago

@annevk

It comes down to #1453 (comment). Platform objects are all defined and created in the same way, but some have overridden internal methods. It would be nice if we could tack those on at the end and not have to know about them upfront.

It is certainly reasonable to factor out common behavior in your specifications. You could define an abstract operation for creating each unique kind of web platform object. If there is a common pattern (for example, common definitions of some of the essential internal methods) followed different kinds of web platform exotic objects you might define an appropriately parameterized abstract operation that could be used to create any one them. It may even be the case that there is only one kind of exotic object that is used for all web platform objects (recall that different kinds of exotic objects are distinguished by difference in their essential internal methods).

Your operations internally create a new object implementing the interface seems like such a definition and roughly comparable to the xxxCreate abstract operations in ES 9.4. Are you saying that there are other places in your specification that create slightly different platform exotic objects and that you are duplicating the steps of this operation? If so, I don't see any reason you couldn't factor the common steps out into a common operation that is shared by all of those usages.

I'm not exactly sure what you mean by "not have to know about them upfront". Do you mean when writing this specific pseudo-code? Or that you might want to start writing other similar definitions without knowing all the details? What exactly is the inconvenience?

allenwb commented 5 years ago

Sure, but if someone wants a shortcut for creating exotic objects, and it comes out looking exactly like ObjectCreate, is there a point to having both?

It would not look the same, in particular, it at least would have a different name indicating a different purpose and a different informative explanation on its intended use within the specification.

The point of ObjectCreate; (and all of the 9.4 xxxCreate operations) is to provide a way for higher abstraction layer operations within the specification to clearly state they are creating an _ordinary object (or specific kind of exotic object). Overloading usage of ObjectCreate would muddy that clear usage intent.

I think we could lessen any misunderstanding by tweaking the prose for ObjectCreate, making it clear that although the object it returns is ordinary, some callers will then make it exotic by changing one or more of its internal methods.

I strongly disagree. Perhaps ObjectCreate should have been named OrdinaryObjectCreate and I would support renaming it. Perhaps, part of it might be factored out into a common definition that might be used by both OrdinaryObjectCreate and some of the xxxCreate exotic object creation operations. But the naming should clearing distinguish operations that are intended for internal use in the xxxCreate operations from the xxxCreate operations that are intended to allow higher abstraction layers of the specification to clearly state what their intent to create specific kinds of objects.

allenwb commented 5 years ago

@annevk

Aye.

I still don't get you issue with the "declarative" style. Could you provide a snippet where I can see how that style would be inconvenient?

zenparsing commented 5 years ago

Perhaps ObjectCreate should have been named OrdinaryObjectCreate and I would support renaming it.

I was just about to suggest that same possibility. : )

If we are consistent about always using xxCreate operations when creating ordinary or exotic objects, then what do you think about removing the special "newly created XXX exotic object" language? Within the xxCreate operations, you'd always just say "newly created object" and then set all the internal methods/slots as appropriate.

jmdyck commented 5 years ago

@allenwb:

Perhaps ObjectCreate should have been named OrdinaryObjectCreate [...]. Perhaps, part of it might be factored out into a common definition that might be used by both OrdinaryObjectCreate and some of the xxxCreate exotic object creation operations.

Okay, so what would you call the factored-out operation?

allenwb commented 5 years ago

what do you think about removing the special "newly created XXX exotic object" language? Within the xxCreate operations, you'd always just say "newly created object"

This seems to ignore the branding aspect of various xxxCreate operations. There are places in the spec. where it needs to explicitly check for various (but not all) specific kinds of exotic objects. We need to have a way to explicitly state which objects are an XXX exotic object. That is the purpose of XXX in the creation phrase. If we eliminate it then we need to add some other way to say, "BTW, this newly created object is identifiable as a XXX exotic object"

and then set all the internal methods/slots as appropriate.

But not all exotic objects necessarily have all the slots of an ordinary object or and may not have slots at all. The only thing required for an entity to be an object is a distinct identity and definitions for the essential internal methods

More in answer to the next @jmdyck message.

allenwb commented 5 years ago

Okay, so what would you call the factored-out operation?

How about AllocateBasicObject.

Where a "basic object" is the foundation for defining ordinary objects and exotic objects that use most of the ordinary object essential internal method definitions.

It would essentially have the current definition of ObjectCreate but should probably explicitly state that its caller needs to explicit reset of any of its essential internal methods that differ from the definitions in clause 9.1.

I'm not sure exactly where in the spec this definition should live. I'm not sure it belongs in 9.1 and I'd be reluctant to renumber the subclauses of 9 to fit it in before it. Perhaps 7.3 would be a good place for it.

Using this new operation, the existing ObjectCreate would just delegate to it, and an operation like ArrayCreate its steps 5-9 would be replaced with something like:

  1. Let A be AllocateBasicObject(proto)
    • A is an array exotic object.
  2. Set A.[[DefineOwnProperty]] as specified in 9.4.2.1.
jmdyck commented 5 years ago

This seems to ignore the branding aspect of various xxxCreate operations. There are places in the spec. where it needs to explicitly check for various (but not all) specific kinds of exotic objects.

Yup.

We need to have a way to explicitly state which objects are an XXX exotic object. That is the purpose of XXX in the creation phrase.

That's what I thought too (see the quote in the original post). E.g., the wording "a newly created Array exotic object" results in an object with some essence that allows you to later identify it as an Array exotic object. And clearly, the wording "a newly created object" doesn't give you that essence. But now I'm thinking maybe we don't need it.

If we eliminate it then we need to add some other way to say, "BTW, this newly created object is identifiable as a XXX exotic object"

Instead, I was thinking that we could replace the last paragraph of 9.4.2 Array Exotic Objects with something like:

An Array exotic object is, by definition, any object whose [[DefineOwnProperty]] internal method is [conforms to] 9.4.2.1, and whose other essential internal methods are the ordinary defaults.

(Might have to say more, not sure.)

Then you can handle the test _x_ is an Array exotic object merely by examining the essential internal methods of _x_, not by relying on a 'brand'.

Then ArrayCreate could start with just "a newly created object" (or a call to AllocateBasicObject) and nevertheless construct an object that qualifies as an Array exotic object.

allenwb commented 5 years ago

Instead, I was thinking that we could replace the last paragraph of 9.4.2 Array Exotic Objects with something like:

In my experience, readers who are searching for clarification tend to miss statements like this that are remote from actual algorithms. How about:

  1. Let A be AllocateBasicObject(proto)
  2. Set A.[[DefineOwnProperty]] as specified in 9.4.2.1.
  3. Assert: A is an Array exotic object.
ljharb commented 5 years ago

"is an Array exotic object" seems pretty loosely defined in the prose in https://tc39.github.io/ecma262/#sec-array-exotic-objects - is there an abstract op-like algorithm that could be used instead of a prose assertion?

allenwb commented 5 years ago

@ljharb

To me, [9.4.3]{https://tc39.github.io/ecma262/#sec-array-exotic-objects) provides a useful and complete description of what the specification means when it says "an Array exotic object".

Sometimes good prose serve the reader better than a set of algorithm steps. In this case, the algorithm steps establish the invariants of Array exoticness described by the prose. But the steps, by themselves don't communicate that those invariants imply Array exoticness. Arguably, the existing ArrayCreate could leave out some of the steps to establish the invariants because the invariants are provided in 9.4.3 and hence implied when it says "create a new array exotic object". But I redundantly included them in the algorithm because past experience suggest that some readers would miss them if they only existed in the prose.

These are editorial decisions relating to how to maximize clarity for different readers of the spec. As a general rule, it is good to minimize unnecessary redundancy in the specification because redundancy, over time, can drift into inconsistency. But we have some readers who are reading algorithm steps as recipes and they are best served by complete and explicit algorithms. Other readers are reading to acquire a conceptual understanding of the essential concepts of the specification. For them, a prose description may work better. For that reason I decided that in the case of the exotic object definitions the redundancy was justified. Do we have any actual non-expert reader feedback suggesting otherwise (we are all experts at reading the specification so I am suspect of all of our own usability perceptions).

ljharb commented 5 years ago

That all makes sense; I’m in no way arguing against the helpful prose, merely suggesting that having both prose and an algorithm may be more helpful.

jmdyck commented 5 years ago

In my experience, readers who are searching for clarification tend to miss statements like this that are remote from actual algorithms.

Okay, but the status quo already has a similar statement that's "remote from actual algorithms", so such a reader would miss it now anyway. What search for clarification do you have in mind, that would be worse off with what I proposed than with the status quo?

How about:

  1. Let A be AllocateBasicObject(proto)
  2. Set A.[[DefineOwnProperty]] as specified in 9.4.2.1.
  3. Assert: A is an Array exotic object.

The definition of "Assert" says that "such assertions add no additional semantic requirements", so you can't use it to 'brand' an object. (But you could use it to inform the reader that the object just constructed satisfies a definition that appears elsewhere.)

jmdyck commented 5 years ago

To me, [9.4.3]{https://tc39.github.io/ecma262/#sec-array-exotic-objects) provides a useful and complete description of what the specification means when it says "an Array exotic object".

I agree it's useful, I don't agree it's complete.

It says: if you have an Array exotic object, then here are a bunch of things that are true of that object. It doesn't say: if you have an object for which those things are true, then it's an Array exotic object.

I.e., it tells you some necessary characteristics of an Array object, but not the sufficient ones.

As a practical example, if an implementation defines an exotic object with certain behaviors, the implementers need to know (e.g., for IsArray()) whether it qualifies as an Array exotic object. But how do they determine this?

rkirsling commented 5 years ago

I agree it's useful, I don't agree it's complete.

I had noted similarly in the final bullet of https://github.com/tc39/ecma262/pull/1212#issuecomment-395663772 (and had meant to improve it accordingly myself). :smile:

allenwb commented 5 years ago

As a practical example, if an implementation defines an exotic object with certain behaviors, the implementers need to know (e.g., for IsArray()) whether it qualifies as an Array exotic object. But how do they determine this?

It is completely up to an implementation how it actually represents and tests whether an arbitrary object is an Array exotic object. But at the level of the specification it is clearly defined which objects most be recognized as Array exotic objects. Within ECMA-262, an object is an Array exotic object if and only if it is created using the ArrayCreate abstract operations. All places in the specification where an Array exotic object is created it is done via ArrayCreate. That is sufficient for expressing the requirements of the specification. When the spec. says "if A is an Array exotic object" it is equivalent to saying " if A was created by ArrayCreate".

The need to be able to recognize an Array exotic object is clear from the fact that the IsArray abstract operation tests whether an arbitrary object is an Array exotic object.

domenic commented 5 years ago

But at the level of the specification it is clearly defined which objects most be recognized as Array exotic objects.

I think the point of this issue is that it is not at all clear. The imprecise "branding" language could be interpreted multiple ways. You say

"if A is an Array exotic object" it is equivalent to saying " if A was created by ArrayCreate"

(call this (1)) but there are at least two other meanings which are entirely plausible from the current spec:

I think (2) is significantly clearer than (1), as we've never given magic significance to abstract operation names before.

zenparsing commented 5 years ago

Ah, right, the implicit branding.

I agree with @allenwb that we should view exotic object creation as "atomic". Logically, at least from the point of view of algorithms that use exotic objects, there ought not be any steps in which a regular object "becomes" an exotic object. That could be accomplished by putting all of the "steps" into xxCreate operations, but then we are left with the branding thing. We could, as @domenic suggests, make the branding checks explicit by testing the internal methods of an object (and likely putting that test in IsX operations).

Alternatively, we could leave branding implicit (@domenic 's 3). But if we leave branding implicit, then it seems to me that there's no good reason to leave the "set these internal methods to x, y, and z" stuff explicit. It's already implied by the definition of "X exotic object".

allenwb commented 5 years ago

@domenic

"if A is an Array exotic object" it is equivalent to saying " if A was created by ArrayCreate"

(call this (1)) but there are at least two other meanings which are entirely plausible from the current spec:

But the spec never does either of those other things so neither can be the meaning in the context of the current specification. This would probably be clearer if ObjetCreate was named OrdinaryObjectCreate. Both @zenparsing and I have stated that renaming would probably be a good idea.

The meta-discussion here is really about how does somebody know what they should write if they need to add a new kind of exotic object to ECMA-262 or a layered related specification. The glib answer follow the conventions currently used within ECMA-262.

The less glib answer is that it would be better if those conventions were more explicitly documented somewhere. There is a certain amount of this in the current specification, but what is there should be focused on what needs to be understood to read the specification. A contributor to the specification need to develop a broader understanding of the conventions in order to write new material. We need to help them develop that understanding.

A "getting started guide to extending the ECMA-262 specification" would be helpful but I don't think we can predict all the authoring questions that will come up. Many of the specification's editorial conventions, like the ones being discussed in this issue, are latent in the current specification and we probably wouldn't think to document them until somebody asks about how to do something specific. Hence, I think it would be great if the current editors started a authoring FAQ where we could capture questions and answers like those discussed in this thread. I suspect that are some old closed issue threads that could be used to seed such a FAQ.

allenwb commented 5 years ago

@zenparsing

But if we leave branding implicit, then it seems to me that there's no good reason to leave the "set these internal methods to x, y, and z" stuff explicit. It's already implied by the definition of "X exotic object".

Yes, setting the internal methods is redundant. For ES6 I tried writing the xxxCreate operations both ways. It seems to me, when I had to go back and read those definition, that the definitions were easier to read when the setting of the internal methods were included as steps of the operations.

domenic commented 5 years ago

I've been working on a pull request that tries to concretize some of the ideas and clarifications in this thread. I think it will make most people happy.

One thing I have run into is that per https://tc39.github.io/ecma262/#sec-ecmascript-function-objects, we say that function objects are ordinary objects. However, they have an overriden [[Call]] internal method. So I guess the definition of "ordinary object" (besides the glib one) is "has the default internal methods, or maybe has a [[Call]] as specified for function objects". Kind of unsatisfying, but I will not change anything for now.

Edit: and [[Construct]] too.

ljharb commented 5 years ago

Is it worth making a “function exotic object” category, to cover those, so ordinary objects would only be ordinary?

domenic commented 5 years ago

It seems like a lot of churn. Maybe less disruptive would be defining an "ordinary non-callable object" category. But, we should wait to see if that is useful to anyone.

At this point I want to put up my PR and see where it takes us before doing anything too disruptive. So far I think my PR is mostly clarification of the type discussed here, with the most disruptive thing being renaming ObjectCreate to OrdinaryObjectCreate.

allenwb commented 5 years ago

@domenic

This was debated when the ordinary/exotic distinction was introduced. TC39 didn't want to say that ECMAScript functions were exotic objects. So the decision was:

The [[Call]] and [[Construct]] are the only two optional essential internal methods.

An ordinary object is one that implements at least the essential internal methods as specified in 9.1. A ordinary function object (AKA an ECMAScript function object) is an ordinary object that also implement the [[Call]] and optionally [[Construct]] methods as specified in 9.2.

I believe that the spec. language reflects this.

domenic commented 5 years ago

For anyone who might not be watching this repo, pull request over in https://github.com/tc39/ecma262/pull/1460 has a concrete proposal. I think it is a nice improvement over the current spec, and look forward to folks' thoughts.

jmdyck commented 5 years ago

@allenwb:

"if A is an Array exotic object" it is equivalent to saying "if A was created by ArrayCreate"

@domenic:

(call this (1)) but there are at least two other meanings which are entirely plausible from the current spec:

@allenwb:

But the spec never does either of those other things so neither can be the meaning in the context of the current specification.

The spec doesn't do (1) either. (It never states that equivalence.)

This would probably be clearer if ObjetCreate was named OrdinaryObjectCreate.

I have no objection to that renaming, but I really don't think it would clarify the meaning of "if A is an Array exotic object".

The meta-discussion here is really about how does somebody know what they should write if they need to add a new kind of exotic object to ECMA-262 or a layered related specification.

That's one possible meta-discussion, but it's not what I'm talking about. An implementation can introduce a new kind of exotic object without changing ECMA-262 or another specification, in which case there isn't a question of how to author new spec-text. But there is the question of how a conforming implementation is required to behave with respect to that object.

jmdyck commented 5 years ago

@ljharb:

Is it worth making a “function exotic object” category, to cover those, so ordinary objects would only be ordinary?

A category by that name already exists (with a different meaning from what you have in mind, I think).

jmdyck commented 5 years ago

@allenwb:

Within ECMA-262, an object is an Array exotic object if and only if it is created using the ArrayCreate abstract operations. All places in the specification where an Array exotic object is created it is done via ArrayCreate.

The second sentence is true, but that doesn't allow you to conclude the first.

That is sufficient for expressing the requirements of the specification.

No, it isn't, because the spec expresses requirements on all objects, not just the ones that it defines.

That is, the criterion "it was created by ArrayCreate" is reasonably well defined for objects whose creation semantics are specified by the spec. But for other objects (novel kinds of exotic objects), what does it even mean? (How does an implementer decide whether any given exotic object is "created by ArrayCreate"?)

allenwb commented 5 years ago

@jmdyck

The second sentence is true, but that doesn't allow you to conclude the first.

Then I suggest that the first sentence be placed within the definition of ArrayCreate

That is, the criterion "it was created by ArrayCreate" is reasonably well defined for objects whose creation semantics are specified by the spec. But for other objects (novel kinds of exotic objects),

ArrayCreate, and everything else in the the specification is a statement of requirements. This means that specifications that extend ECMA-262 must explicitly reference 262's ArrayCreate if they need to specify the instantiation of an object as an "Array exotic object" and hence be recognized as such by the parts of the ECMA-262 that test for "Array exotic object".

This is largely the whole point of having operations like ArrayCreate. Other novel kinds of exotic objects are not "Array exotic objets" and must not be recognized as such by conforming ECMA-262 implementations.

Practically speaking, that means that if you introduce some new kind of exotic object not created in conformance with ArrayCreate but for which Array.isArray should return true you will be modifying the specified behavior of Array.isArray. Whether or not such a modification is n conforming extension permitted by Clause 16 is probably a separate discussion.

what does it even mean? (How does an implementer decide whether any given exotic object is "created by ArrayCreate"?)

Implementations define their own mechanisms for fulfilling the requirements of ECMA-262 and other specifications. An implementation may have a single piece of code that implements ArrayCreate or many separately implement it multiple places. It may have some sort of explicit tag meaning this object was created in compliance with ArrayCreate or it may infer it some other way. None of this is relevant to the specification. How an implementation decides to designate and recognize objects "created by ArrayCreate" is its own design decision.

jmdyck commented 5 years ago

Then I suggest that the first sentence [i.e., "Within ECMA-262, an object is an Array exotic object if and only if it is created using the ArrayCreate abstract operations."] be placed within the definition of ArrayCreate

I still don't think that wording is as clear as you think it is.

[...] specifications that extend ECMA-262 must explicitly reference 262's ArrayCreate if they need to specify the instantiation of an object as an "Array exotic object" and hence be recognized as such by the parts of the ECMA-262 that test for "Array exotic object".

That doesn't address my point, because I'm not talking about "specifications that extend ECMA-262". As I said before, an implementation can introduce a new kind of exotic object without writing a specification that extends ECMA-262.

This is largely the whole point of having operations like ArrayCreate. Other novel kinds of exotic objects are not "Array exotic objets" and must not be recognized as such by conforming ECMA-262 implementations.

That last sentence is pretty much a tautology: objects other than Array exotic objects are not Array exotic objects. I think your position is something like: the set of objects that qualify as "Array exotic objects" includes only those objects that are behaviorally indistinguishable from the Array objects created by the spec. Which is certainly a valid stance, but I don't think the spec is clear on the point (and there doesn't appear to be committee consensus).

What puzzles me is why you didn't object to Domenic's much wider definition of the term in PR #1460.

(How does an implementer decide whether any given exotic object is "created by ArrayCreate"?)

Implementations define their own mechanisms for fulfilling the requirements of ECMA-262 and other specifications. An implementation may have a single piece of code that implements ArrayCreate or many separately implement it multiple places. It may have some sort of explicit tag meaning this object was created in compliance with ArrayCreate or it may infer it some other way. None of this is relevant to the specification.

Agreed, but you've misunderstood my question again. I wasn't asking how to implement something, I was asking what constitutes conformant behavior.