New ES6 draft now available
Allen Wirfs-Brock wrote:
The Nov. 22, 2012 draft of the ES6 specificaiotn is now at harmony:specification_drafts
Clicking "pdf with change markup only for this revision" yields "Not Found".
fixed
I haven't been following very closely some of the most recent discussions, so I appologize if my comments have been addressed already
Le 23/11/2012 18:48, Allen Wirfs-Brock a écrit :
Changes include:
. Reorganized Chapter 8 into separate language type and specification type sub sections . Added Symbol property keys to the definition of the Object Type. terminology migration from talking about "property names" to "property keys".
I support this change. It's somewhat minor, but clearly indicates that a shift has occured.
. MOP changes: Added [[GetInheritance]]/[[SetInheritance]] as internal methods for accessing [[Prototype]] internal prototype chain.
Why not [[GetPrototype]] and [[SetPrototype]]? We have a absurd number of excellent resources (including but not limited to Dmitry Soshnikov and Axel Rauschmayer's websites/blogs) which use extensively the ES5 internal [[Prototype]]. It's also been very hard to get people to understand the difference between .prototype and [[Prototype]]. I'm afraid a new change would create a lot of confusion.
Added [[[IsExtensible]]/[[PreventExtensions]].
ES5 had a note mentioning that there was no standard mechanism to set [[Extensible]] from false to true; this new way makes the note unnecessary by design of having internal methods instead of an internal property. OCaps FTW I guess :-)
Replaced [[Get]]/[[Put]] with [[GetP]]/[[SetP]]. At some point we may be able to rename these as [[Get]]/[[Set]]. +1 for [[Get]]/[[Set]]. That's the accessor terminology, that's what JS devs are used to. It makes the spec easier to read for them.
Eliminated [[GetProperty]], [[CanPut]], [[HasProperty]], [[DefaultValue]].
So good to see this cleanup :-)
Added Keys, [[OwnPropertyKeys]], [[Freeze]], [[Seal]], [[IsFrozen]], [[IsSealed]].
For both the enumerations and the layer on top of [[PreventExtension]], I'm uneasy.
- [[Enumerate]], [[Keys]] and [[OwnPropertyKeys]] are very close operations
- So are [[PreventExtensions]]/[[Freeze]]/[[Seal]] on one side and [[IsExtensible]]/[[IsFrozen]]/[[IsSealed]]
I'm afraid that making them distinct operations increases footgun-ness. [[HasProperty]] has been removed in favored of [[HasOwnProperty]] (which might be removed in favor of only keeping [[GetOwnProperty]], though the conclusion of the discussion was the keep both IIRC) because the former could be "robustly" composed between
For proxies, [[Extensible]]ity-related operations have invariants so inconsistencies between [[PreventExtensions]], [[Freeze]], [[Seal]], [[IsExtensible]], [[IsFrozen]] and [[IsSealed]] are impossible. However, one can legitimately wonder what will happen if a new built-in operation using [[PreventExtensions]] is introduced in a later version of ECMAScript. Let's say experience with unique/private symbols reveals that the current Object.freeze behavior (whatever it is in regard to symbols) is inappropriate and that adding a new Object.freezeWithDifferentSymbolHandling is introduced. The current pattern suggests that adding new [[FreezeWithDifferentSymbolHandling]]/[[IsFrozenWithDifferentSymbolHandling]] internals will be the way to go. I'm not sure it's a good pattern.
The issue seems worse in footgunness scale for enumeration operations because different operations could yield completely unrelated results. And once again, if it is wanted to add a new enumeration operation on syntax surface, it seems that a new internal operation will be necessary.
Here is an idea to uniformize the enumeration story while removing enumeration inconsistency footgun. I'll describe it in proxy trap terms. A unique trap (or internal operation) keyEnumerate: () -> iterator for {propertyKey, enumerable}
There is this unique operation which returns an iterator not for property keys, but for an object containing a property key and a boolean signifying the enumerable boolean. Using this, each userland operation would use this iterator, drain it out (unless the iteration is being aborted like by throwing in a for-in loop) and filter out based on the exact userland operation so that:
- All would filter out if propertyKey is ever a private symbol
- Object.getOwnPropertyNames would keep all other property keys regardless of the enumerable value
- Object.keys would filter out properties which are described as non-enumerable
- for-in would filter out non-enumerable and retrieve the different keyEnumerate from the prototype chain objects. With this unique internal operation, an object is able to communicate its intent regarding what's enumerated and each enumeration userland operation only keeps what it's interested in.
. instanceof operator no longer uses an internal method. Instead, extensible via a @@hasInstance symbol-keyed regular method.
I don't remember a discussion on this topic, but I think it's an interesting change. Is it for browser APIs? There might be an associated security worry here if any object can have its @@hasInstance changed, but one good thing is that the debate of trapping instanceof ends if it's a symbol-keyed method.
. Defined all of the functions in the @Reflect module corresponding to MOP internal operations.
IIRC proto has been agreed to be an data property and there was no Reflect.setPrototypeOf operation in the harmony Reflect module proposal on purpose so that 'delete Object.prototype.proto' at any point would be enough to be sure that all [[Prototype]]-setting capabilities are removed. So I think the Reflect.setPrototypeOf should be removed.
• MOP changes: Added [[GetInheritance]]/[[SetInheritance]] as internal methods for accessing [[Prototype]] internal prototype chain. Why not [[GetPrototype]] and [[SetPrototype]]? We have a absurd number of excellent resources (including but not limited to Dmitry Soshnikov and Axel Rauschmayer's websites/blogs) which use extensively the ES5 internal [[Prototype]]. It's also been very hard to get people to understand the difference between .prototype and [[Prototype]]. I'm afraid a new change would create a lot of confusion.
Currently, we have the following inheritance-related terms:
- [[Prototype]]
- [[GetInheritance]], [[SetInheritance]]
- proto
- Object.getPrototypeOf()
- C.prototype
1-4 are closely related, 5 refers to a different concept. I still welcome any measure to make this terminology easier to understand and talk about, but I realize that that might not be possible.
Axel
David Bruant wrote:
Hi,
I haven't been following very closely some of the most recent discussions, so I appologize if my comments have been addressed already
Le 23/11/2012 18:48, Allen Wirfs-Brock a écrit :
Changes include:
• MOP changes: Added [[GetInheritance]]/[[SetInheritance]] as internal methods for accessing [[Prototype]] internal prototype chain. Why not [[GetPrototype]] and [[SetPrototype]]? We have a absurd number of excellent resources (including but not limited to Dmitry Soshnikov and Axel Rauschmayer's websites/blogs) which use extensively the ES5 internal [[Prototype]]. It's also been very hard to get people to understand the difference between .prototype and [[Prototype]]. I'm afraid a new change would create a lot of confusion.
Neither of them is prototype, so the naming is unhappy. It is good that renaming is happening. And [[Prototype]] is imo worse of the two (the second being .prototype), so I welcome the fact that the operations were named as such. I hope the next step is to rename [[Prototype]] itself to [[Inheritance]]. Fair share of uses of [[Prototype]] were deleted in this change, and from those that left the most are in Chapter 15 where they are used mechanically to spec builtins.
Le 24/11/2012 15:23, Herby Vojčík a écrit :
David Bruant wrote:
Hi,
I haven't been following very closely some of the most recent discussions, so I appologize if my comments have been addressed already
Le 23/11/2012 18:48, Allen Wirfs-Brock a écrit :
Changes include:
• MOP changes: Added [[GetInheritance]]/[[SetInheritance]] as internal methods for accessing [[Prototype]] internal prototype chain. Why not [[GetPrototype]] and [[SetPrototype]]? We have a absurd number of excellent resources (including but not limited to Dmitry Soshnikov and Axel Rauschmayer's websites/blogs) which use extensively the ES5 internal [[Prototype]]. It's also been very hard to get people to understand the difference between .prototype and [[Prototype]]. I'm afraid a new change would create a lot of confusion.
Neither of them is prototype
I don't understand this sentence. What does "them" refer to?
Le 24/11/2012 14:58, Axel Rauschmayer a écrit :
• MOP changes: Added [[GetInheritance]]/[[SetInheritance]] as internal methods for accessing [[Prototype]] internal prototype chain. Why not [[GetPrototype]] and [[SetPrototype]]? We have a absurd number of excellent resources (including but not limited to Dmitry Soshnikov and Axel Rauschmayer's websites/blogs) which use extensively the ES5 internal [[Prototype]]. It's also been very hard to get people to understand the difference between .prototype and [[Prototype]]. I'm afraid a new change would create a lot of confusion.
Currently, we have the following inheritance-related terms:
- [[Prototype]]
- [[GetInheritance]], [[SetInheritance]]
- proto
- Object.getPrototypeOf()
The fact that Object.getPrototypeOf retrieves the [[Prototype]] (ES5) or delegates to [[GetPrototype]] (ES6?) makes sense. I find delegating to [[GetInheritance]] more debatable.
- C.prototype
1-4 are closely related, 5 refers to a different concept. I still welcome any measure to make this terminology easier to understand and talk about, but I realize that that might not be possible.
As far as ES6 is concerned, it's likely that the 5 form will disappear because classes will cover most of the basic needs. .prototype will become useful only for people who know what they're doing. But ES6 in production is far down the road...
On Nov 24, 2012, at 6:23 AM, Herby Vojčík wrote:
David Bruant wrote:
Hi,
I haven't been following very closely some of the most recent discussions, so I appologize if my comments have been addressed already
Le 23/11/2012 18:48, Allen Wirfs-Brock a écrit :
Changes include:
• MOP changes: Added [[GetInheritance]]/[[SetInheritance]] as internal methods for accessing [[Prototype]] internal prototype chain. Why not [[GetPrototype]] and [[SetPrototype]]? We have a absurd number of excellent resources (including but not limited to Dmitry Soshnikov and Axel Rauschmayer's websites/blogs) which use extensively the ES5 internal [[Prototype]]. It's also been very hard to get people to understand the difference between .prototype and [[Prototype]]. I'm afraid a new change would create a lot of confusion.
Neither of them is prototype, so the naming is unhappy. It is good that renaming is happening. And [[Prototype]] is imo worse of the two (the second being .prototype), so I welcome the fact that the operations were named as such. I hope the next step is to rename [[Prototype]] itself to [[Inheritance]]. Fair share of uses of [[Prototype]] were deleted in this change, and from those that left the most are in Chapter 15 where they are used mechanically to spec builtins.
This is largely now the case. Other than the Chapter 15 boiler plate all generic access should now be expressed as [[GetInheriance]]. The only place there should now be direct reference to [[Prototype]] are in the implementation of the MOP interface of ordinary objects or exotic objects that also have a [[Prototype]]. Ultimately these should all be in the currently numbered sections 8.3-8.5.
I need to think about what we should do in chapter 15. Because class definitions use the work "extends" perhaps we can define some boiler place language that uses that verb.
On Nov 24, 2012, at 8:43 AM, David Bruant wrote:
Le 24/11/2012 14:58, Axel Rauschmayer a écrit :
• MOP changes: Added [[GetInheritance]]/[[SetInheritance]] as internal methods for accessing [[Prototype]] internal prototype chain. Why not [[GetPrototype]] and [[SetPrototype]]? We have a absurd number of excellent resources (including but not limited to Dmitry Soshnikov and Axel Rauschmayer's websites/blogs) which use extensively the ES5 internal [[Prototype]]. It's also been very hard to get people to understand the difference between .prototype and [[Prototype]]. I'm afraid a new change would create a lot of confusion.
Currently, we have the following inheritance-related terms:
- [[Prototype]]
- [[GetInheritance]], [[SetInheritance]]
- proto
- Object.getPrototypeOf() The fact that Object.getPrototypeOf retrieves the [[Prototype]] (ES5) or delegates to [[GetPrototype]] (ES6?) makes sense. I find delegating to [[GetInheritance]] more debatable.
This is a point that TomV and I have debated. Tom also thinks that the MOP/Proxy trap/Reflect.* naming should follow the precedents set by ES<=5. My position is that these legacy functions are at a higher layer than the MOP and hence do not necessarily need to follow the same conventions. Also the current names are not all that consistent and don't reflect our current deeper understanding of JS objects (including DOM legacy). I'd prefer to keep the MOPish layer (including Proxy traps and Reflect.*) internally consistent and where necessary or beneficial deviate from legacy naming used at higher abstraction layers.
- C.prototype
1-4 are closely related, 5 refers to a different concept. I still welcome any measure to make this terminology easier to understand and talk about, but I realize that that might not be possible. As far as ES6 is concerned, it's likely that the 5 form will disappear because classes will cover most of the basic needs. .prototype will become useful only for people who know what they're doing. But ES6 in production is far down the road...
Yes, but everything we are talking about here is specifically about ES6 and beyond...
David Bruant wrote:
Hi,
I haven't been following very closely some of the most recent discussions, so I appologize if my comments have been addressed already
Le 23/11/2012 18:48, Allen Wirfs-Brock a écrit :
Changes include:
• Reorganized Chapter 8 into separate language type and specification type sub sections • Added Symbol property keys to the definition of the Object Type. terminology migration from talking about “property names” to “property keys”. I support this change. It's somewhat minor, but clearly indicates that a shift has occured.
+1
• MOP changes: Added [[GetInheritance]]/[[SetInheritance]] as internal methods for accessing [[Prototype]] internal prototype chain. Why not [[GetPrototype]] and [[SetPrototype]]? We have a absurd number of excellent resources (including but not limited to Dmitry Soshnikov and Axel Rauschmayer's websites/blogs) which use extensively the ES5 internal [[Prototype]]. It's also been very hard to get people to understand the difference between .prototype and [[Prototype]]. I'm afraid a new change would create a lot of confusion.
Yes, please -- let's not change names gratuitously. [[Prototype]] stays, so these should be related names.
Added [[[IsExtensible]]/[[PreventExtensions]]. ES5 had a note mentioning that there was no standard mechanism to set [[Extensible]] from false to true; this new way makes the note unnecessary by design of having internal methods instead of an internal property. OCaps FTW I guess :-)
Or just a restrictive MOP FTW ;-).
Replaced [[Get]]/[[Put]] with [[GetP]]/[[SetP]]. At some point we may be able to rename these as [[Get]]/[[Set]]. +1 for [[Get]]/[[Set]]. That's the accessor terminology, that's what JS devs are used to. It makes the spec easier to read for them.
Yes, I assumed the -P suffixing was a temporary renaming aid. Allen, can you confirm?
Eliminated [[GetProperty]], [[CanPut]], [[HasProperty]], [[DefaultValue]]. So good to see this cleanup :-)
+10!
Here is an idea to uniformize the enumeration story while removing enumeration inconsistency footgun. I'll describe it in proxy trap terms. A unique trap (or internal operation) keyEnumerate: () -> iterator for {propertyKey, enumerable} There is this unique operation which returns an iterator not for property keys, but for an object containing a property key and a boolean signifying the enumerable boolean. Using this, each userland operation would use this iterator, drain it out (unless the iteration is being aborted like by throwing in a for-in loop) and filter out based on the exact userland operation so that:
- All would filter out if propertyKey is ever a private symbol
- Object.getOwnPropertyNames would keep all other property keys regardless of the enumerable value
- Object.keys would filter out properties which are described as non-enumerable
- for-in would filter out non-enumerable and retrieve the different keyEnumerate from the prototype chain objects. With this unique internal operation, an object is able to communicate its intent regarding what's enumerated and each enumeration userland operation only keeps what it's interested in.
As an internal method architecture for the spec, this seems fine -- the "object" returned cannot be observed and could be an internal type, a la Reference.
For a proxy trap, this seems too low-level and the overhead (without optimization to look into the continuation for a destructuring pattern and avoid the object allocation) is again a potential problem. Tom should weigh in.
• instanceof operator no longer uses an internal method. Instead, extensible via a @@hasInstance symbol-keyed regular method. I don't remember a discussion on this topic, but I think it's an interesting change. Is it for browser APIs?
Yes, the DOM specifically, and WebIDL in general.
There might be an associated security worry here if any object can have its @@hasInstance changed, but one good thing is that the debate of trapping instanceof ends if it's a symbol-keyed method.
Proxies have trap integrity, that is, you can't mess with their handlers to mutate traps unless you are granted the handler capability. Right?
• Defined all of the functions in the @Reflect module corresponding to MOP internal operations. IIRC proto has been agreed to be an data property
The jury is still out (could be accessor with poisoned reflection), but this is not material to your point.
and there was no Reflect.setPrototypeOf operation in the harmony Reflect module proposal on purpose so that 'delete Object.prototype.proto' at any point would be enough to be sure that all [[Prototype]]-setting capabilities are removed. So I think the Reflect.setPrototypeOf should be removed.
Yes, we covered this on-list extensively. There should be no *.setPrototypeOf function anywhere.
Le 25/11/2012 01:32, Brendan Eich a écrit :
Great to see a first draft of the Proxy spec. I'll study this revision more closely over the coming week.
Some initial comments below:
2012/11/23 Allen Wirfs-Brock <allen at wirfs-brock.com>
• MOP changes: Added [[GetInheritance]]/[[SetInheritance]] as internal methods for accessing [[Prototype]] internal prototype chain.
I would rename these to [[GetProto]]/[[SetProto]] (or [[GetPrototype]] as David suggests).
Added [[[IsExtensible]]/[[PreventExtensions]].
Replaced [[Get]]/[[Put]] with [[GetP]]/[[SetP]]. At some point we may be able to rename these as [[Get]]/[[Set]].
+1 for [[Get]]/[[Set]]. The P suffix was indeed to distinguish this operation from ES5.1 [[Get]].
Eliminated [[GetProperty]], [[CanPut]], [[HasProperty]], [[DefaultValue]].
Great!
However, with [[HasProperty]] removed, I assume the "in"-operator no longer triggers the "has" trap when it encounters a proxy, instead doing the proto-chain-walk itself and calling "hasOwn" on each level. This is OK, although it introduces an inconsistency with the other proto-chain-walking algorithms, which stop when encountering a proxy, letting the proxy take over from that point (this is the case for the "get", "set" and "enumerate" traps).
2012/11/24 David Bruant <bruant.d at gmail.com>
Le 23/11/2012 18:48, Allen Wirfs-Brock a écrit :
Added Keys, [[OwnPropertyKeys]], [[Freeze]], [[Seal]], [[IsFrozen]], [[IsSealed]].
For both the enumerations and the layer on top of [[PreventExtension]], I'm uneasy.
- [[Enumerate]], [[Keys]] and [[OwnPropertyKeys]] are very close operations
- So are [[PreventExtensions]]/[[Freeze]]/[[Seal]] on one side and [[IsExtensible]]/[[IsFrozen]]/[[IsSealed]]
I'm afraid that making them distinct operations increases footgun-ness. [[HasProperty]] has been removed in favored of [[HasOwnProperty]] (which might be removed in favor of only keeping [[GetOwnProperty]], though the conclusion of the discussion was the keep both IIRC) because the former could be "robustly" composed between
The way I see it:
- we have no way of enforcing fully internally consistent proxies anyway
- even with some traps removed (such as "has"), opportunities for inconsistencies abound
- thus, why be selective and try to combat some inconsistencies while letting others slip by?
The Handler API seems an adequate solution to avoid the footgun of trap inconsistencies.
• instanceof operator no longer uses an internal method. Instead, extensible via a @@hasInstance symbol-keyed regular method.
I don't remember a discussion on this topic, but I think it's an interesting change. Is it for browser APIs? There might be an associated security worry here if any object can have its @@hasInstance changed, but one good thing is that the debate of trapping instanceof ends if it's a symbol-keyed method.
I don't think there's a security issue: can't symbol-keyed properties also be made non-configurable?
2012/11/24 Allen Wirfs-Brock <allen at wirfs-brock.com>
On Nov 24, 2012, at 8:43 AM, David Bruant wrote:
The fact that Object.getPrototypeOf retrieves the [[Prototype]] (ES5) or delegates to [[GetPrototype]] (ES6?) makes sense. I find delegating to [[GetInheritance]] more debatable.
This is a point that TomV and I have debated. Tom also thinks that the MOP/Proxy trap/Reflect.* naming should follow the precedents set by ES<=5. My position is that these legacy functions are at a higher layer than the MOP and hence do not necessarily need to follow the same conventions. Also the current names are not all that consistent and don't reflect our current deeper understanding of JS objects (including DOM legacy). I'd prefer to keep the MOPish layer (including Proxy traps and Reflect.*) internally consistent and where necessary or beneficial deviate from legacy naming used at higher abstraction layers.
My preference is indeed to stick with ES5 terminology, especially if there already is good user-facing terminology. ES5 has Object.getPrototypeOf, the most consistent would thus be to use [[GetPrototypeOf]].
2012/11/25 Brendan Eich <brendan at mozilla.org>
David Bruant wrote:
Here is an idea to uniformize the enumeration story while removing enumeration inconsistency footgun. I'll describe it in proxy trap terms. A unique trap (or internal operation)
keyEnumerate: () -> iterator for {propertyKey, enumerable}
There is this unique operation which returns an iterator not for property keys, but for an object containing a property key and a boolean signifying the enumerable boolean. Using this, each userland operation would use this iterator, drain it out (unless the iteration is being aborted like by throwing in a for-in loop) and filter out based on the exact userland operation so that:
- All would filter out if propertyKey is ever a private symbol
- Object.getOwnPropertyNames would keep all other property keys regardless of the enumerable value
- Object.keys would filter out properties which are described as non-enumerable
- for-in would filter out non-enumerable and retrieve the different keyEnumerate from the prototype chain objects. With this unique internal operation, an object is able to communicate its intent regarding what's enumerated and each enumeration userland operation only keeps what it's interested in.
As an internal method architecture for the spec, this seems fine -- the "object" returned cannot be observed and could be an internal type, a la Reference.
For a proxy trap, this seems too low-level and the overhead (without optimization to look into the continuation for a destructuring pattern and avoid the object allocation) is again a potential problem. Tom should weigh in.
I'm not too worried about inconsistencies between traps. I don't see it as sufficient reason to refactor the current getOwnPropertyNames/keys/enumerate trap design.
Of course I'm open to alternatives if people think it is a major issue.
and there was no Reflect.setPrototypeOf operation in the harmony Reflect
module proposal on purpose so that 'delete Object.prototype.proto' at any point would be enough to be sure that all [[Prototype]]-setting capabilities are removed. So I think the Reflect.setPrototypeOf should be removed.
Yes, we covered this on-list extensively. There should be no *.setPrototypeOf function anywhere.
I agree. I don't think we want Reflect.setPrototypeOf.
On Nov 25, 2012, at 12:41 PM, Tom Van Cutsem wrote:
Hi Allen,
Great to see a first draft of the Proxy spec. I'll study this revision more closely over the coming week.
Some initial comments below:
2012/11/23 Allen Wirfs-Brock <allen at wirfs-brock.com>
• MOP changes: Added [[GetInheritance]]/[[SetInheritance]] as internal methods for accessing [[Prototype]] internal prototype chain.
I would rename these to [[GetProto]]/[[SetProto]] (or [[GetPrototype]] as David suggests).
Added [[[IsExtensible]]/[[PreventExtensions]].
Replaced [[Get]]/[[Put]] with [[GetP]]/[[SetP]]. At some point we may be able to rename these as [[Get]]/[[Set]].
+1 for [[Get]]/[[Set]]. The P suffix was indeed to distinguish this operation from ES5.1 [[Get]].
Eliminated [[GetProperty]], [[CanPut]], [[HasProperty]], [[DefaultValue]].
Great!
However, with [[HasProperty]] removed, I assume the "in"-operator no longer triggers the "has" trap when it encounters a proxy, instead doing the proto-chain-walk itself and calling "hasOwn" on each level. This is OK, although it introduces an inconsistency with the other proto-chain-walking algorithms, which stop when encountering a proxy, letting the proxy take over from that point (this is the case for the "get", "set" and "enumerate" traps).
[[HasProperty]] is replaced with a HasProperty abstract operation (9.3.6) that indeed makes generic calls to [[HasOwnProperty]] and [[GetInheritance]] in a loop. This also slightly bothers me. I guess I convinced my self that an object that wanted to use a non-standard inheritance path could hide that by reporting its inherited properties to be own properties.
I also occasionally find it troubling inheritance semantics are replicated in three different places [[GetP]], [[SetP]], and HasProperty and that it is quite possible to get them out of sync. I've come to accept the replication/possible inconsistency but maybe we really do need to bring back [[HasProperty]] so that it is possible to consistently over-ride all three. I'd might be happy to swap [[Feeze]]/[[Seal]]/i[[sFrozen]]/[[isSealed]] for [[HasProperty]] :-)
2012/11/25 Allen Wirfs-Brock <allen at wirfs-brock.com>
On Nov 25, 2012, at 12:41 PM, Tom Van Cutsem wrote:
However, with [[HasProperty]] removed, I assume the "in"-operator no longer triggers the "has" trap when it encounters a proxy, instead doing the proto-chain-walk itself and calling "hasOwn" on each level. This is OK, although it introduces an inconsistency with the other proto-chain-walking algorithms, which stop when encountering a proxy, letting the proxy take over from that point (this is the case for the "get", "set" and "enumerate" traps).
[[HasProperty]] is replaced with a HasProperty abstract operation (9.3.6) that indeed makes generic calls to [[HasOwnProperty]] and [[GetInheritance]] in a loop. This also slightly bothers me. I guess I convinced my self that an object that wanted to use a non-standard inheritance path could hide that by reporting its inherited properties to be own properties.
I also occasionally find it troubling inheritance semantics are replicated in three different places [[GetP]], [[SetP]], and HasProperty and that it is quite possible to get them out of sync. I've come to accept the replication/possible inconsistency but maybe we really do need to bring back [[HasProperty]] so that it is possible to consistently over-ride all three. I'd might be happy to swap [[Feeze]]/[[Seal]]/i[[sFrozen]]/[[isSealed]] for [[HasProperty]] :-)
Yes, this is a good point: one must be able to consistently override all inheritance-related operations. Requiring an object with multiple prototypes to report all inherited properties as "own" feels broken.
W.r.t. freeze and friends, I thought you were considering a more generic [[GetIntegrity]]/[[SetIntegrity]] operation.
On Nov 26, 2012, at 9:44 AM, Tom Van Cutsem wrote:
2012/11/25 Allen Wirfs-Brock <allen at wirfs-brock.com>
On Nov 25, 2012, at 12:41 PM, Tom Van Cutsem wrote:
However, with [[HasProperty]] removed, I assume the "in"-operator no longer triggers the "has" trap when it encounters a proxy, instead doing the proto-chain-walk itself and calling "hasOwn" on each level. This is OK, although it introduces an inconsistency with the other proto-chain-walking algorithms, which stop when encountering a proxy, letting the proxy take over from that point (this is the case for the "get", "set" and "enumerate" traps).
[[HasProperty]] is replaced with a HasProperty abstract operation (9.3.6) that indeed makes generic calls to [[HasOwnProperty]] and [[GetInheritance]] in a loop. This also slightly bothers me. I guess I convinced my self that an object that wanted to use a non-standard inheritance path could hide that by reporting its inherited properties to be own properties.
I also occasionally find it troubling inheritance semantics are replicated in three different places [[GetP]], [[SetP]], and HasProperty and that it is quite possible to get them out of sync. I've come to accept the replication/possible inconsistency but maybe we really do need to bring back [[HasProperty]] so that it is possible to consistently over-ride all three. I'd might be happy to swap [[Feeze]]/[[Seal]]/i[[sFrozen]]/[[isSealed]] for [[HasProperty]] :-)
Yes, this is a good point: one must be able to consistently override all inheritance-related operations. Requiring an object with multiple prototypes to report all inherited properties as "own" feels broken.
W.r.t. freeze and friends, I thought you were considering a more generic [[GetIntegrity]]/[[SetIntegrity]] operation.
Yes, are you on board with that?
2012/11/26 Allen Wirfs-Brock <allen at wirfs-brock.com>
On Nov 26, 2012, at 9:44 AM, Tom Van Cutsem wrote:
W.r.t. freeze and friends, I thought you were considering a more generic [[GetIntegrity]]/[[SetIntegrity]] operation.
Yes, are you on board with that?
Yes, I haven't yet considered all the implications, but it seems fine.
The initial discussion was about the usefulness of being able to trap freeze/isFrozen. I argued that having these traps allows proxies to more efficiently implement those operations (the alternative being to call defineProperty/getOwnPropertyDescriptor on each individual property).
Having [GetIntegrity]//[SetIntegrity] (with corresponding traps) retains the ability for proxies to efficiently change their state to frozen, or return an answer to an integrity query. It also makes the internal states of an object more explicit in the specification, which is good.
I'm skeptical though, whether this change achieves your original goal of reducing the risk of inconsistencies between the different operations. It depends on whether you think doing a case-analysis using switch or if-tests is less error-prone than having to override/implement multiple different traps.
On Nov 26, 2012, at 10:56 AM, Tom Van Cutsem wrote:
2012/11/26 Allen Wirfs-Brock <allen at wirfs-brock.com>
On Nov 26, 2012, at 9:44 AM, Tom Van Cutsem wrote:
W.r.t. freeze and friends, I thought you were considering a more generic [[GetIntegrity]]/[[SetIntegrity]] operation.
Yes, are you on board with that?
Yes, I haven't yet considered all the implications, but it seems fine.
The initial discussion was about the usefulness of being able to trap freeze/isFrozen. I argued that having these traps allows proxies to more efficiently implement those operations (the alternative being to call defineProperty/getOwnPropertyDescriptor on each individual property).
Having [GetIntegrity]//[SetIntegrity] (with corresponding traps) retains the ability for proxies to efficiently change their state to frozen, or return an answer to an integrity query. It also makes the internal states of an object more explicit in the specification, which is good.
I'm skeptical though, whether this change achieves your original goal of reducing the risk of inconsistencies between the different operations. It depends on whether you think doing a case-analysis using switch or if-tests is less error-prone than having to override/implement multiple different traps.
Yes, I do think case analysis in a single function is less error-prone. I think logic that is centralized to a single function is more likely to self-consistent than logic that has to be duplicated in multiple functions. Particularly, when it is possible to redefine the multiple functions individually without being forced to redefine the entire set.
2012/11/26 Allen Wirfs-Brock <allen at wirfs-brock.com>
On Nov 26, 2012, at 10:56 AM, Tom Van Cutsem wrote:
I'm skeptical though, whether this change achieves your original goal of reducing the risk of inconsistencies between the different operations. It depends on whether you think doing a case-analysis using switch or if-tests is less error-prone than having to override/implement multiple different traps.
Yes, I do think case analysis in a single function is less error-prone. I think logic that is centralized to a single function is more likely to self-consistent than logic that has to be duplicated in multiple functions. Particularly, when it is possible to redefine the multiple functions individually without being forced to redefine the entire set.
It's all a matter of trade-offs. One thing that we lose by bundling everything into a single trap is the ability to easily add new traps with default semantics. One of the reasons for baking the Handler API into the spec was that we could add new derived traps in ES7+, and Proxy abstractions whose handler subclasses Handler would inherit a sensible default implementation of these new derived traps.
When dealing with a getIntegrity/setIntegrity trap, it's not easy to add a new object state later: the existing case-analysis code won't be prepared to deal with it.
On Nov 27, 2012, at 6:19 AM, Tom Van Cutsem wrote:
2012/11/26 Allen Wirfs-Brock <allen at wirfs-brock.com>
On Nov 26, 2012, at 10:56 AM, Tom Van Cutsem wrote:
I'm skeptical though, whether this change achieves your original goal of reducing the risk of inconsistencies between the different operations. It depends on whether you think doing a case-analysis using switch or if-tests is less error-prone than having to override/implement multiple different traps.
Yes, I do think case analysis in a single function is less error-prone. I think logic that is centralized to a single function is more likely to self-consistent than logic that has to be duplicated in multiple functions. Particularly, when it is possible to redefine the multiple functions individually without being forced to redefine the entire set.
It's all a matter of trade-offs. One thing that we lose by bundling everything into a single trap is the ability to easily add new traps with default semantics. One of the reasons for baking the Handler API into the spec was that we could add new derived traps in ES7+, and Proxy abstractions whose handler subclasses Handler would inherit a sensible default implementation of these new derived traps.
When dealing with a getIntegrity/setIntegrity trap, it's not easy to add a new object state later: the existing case-analysis code won't be prepared to deal with it.
The basic argument would seem to apply to any relatively complex trap/internal method semantics. For example, if we added a new attribute to property descriptors or and enhanced semantics for property get/put (for example, comparable to the changes that we necessarily to originally add accessor properties). In this particular case, a well designed setIntegrity trap handler should probably just forward any unrecognized states on to Reflect.setIntegrity(target,state)
We making trade-off across several dimensions: size of the MOP, ease of extensions, minimizing chance of inconsistencies, etc. It feels to me like setIntegrity would be a net positive improvement, but that is certainly open to debate.
The Nov. 22, 2012 draft of the ES6 specificaiotn is now at harmony:specification_drafts
Here are the release notes:
Changes marked as “Rev 12”
This is a major revision that takes the first step at reforming the internal Meta-Object Protocol (the “internal methods”) and extending it to support Proxies. Quite a bit of material has moved within the document. The overall document organization is in a state of flux and some sections will be moved in the future. For now, somethings have been temporarily left in their old locations to preserve change markup relative to ES 5.1 that might otherwise get lost.
Changes include: