Re: Public/private namespaces in harmony classes proposal
On Jul 7, 2011, at 9:49 PM, Mark S. Miller wrote: On Thu, Jul 7, 2011 at 9:41 PM, Gavin Barraclough barraclo...@apple.com wrote: Hi Mark, In the harmony classes proposal, http://wiki.ecmascript.org/doku.php?id=harmony:classes , I'm interested in understanding the following issue: One or two namespaces for public properties and private instance variables [RESOLVED two, Mark’s argument] Do you remember if this argument was made in email, and if so would anyone happen to know where to look to find this (I've tried a little googling to no avail!), I'd be interested in understanding the rationale behind this decision. I don't think it was made in before in email. Here goes: For non-const classes, their instances are extensible by default. Even if you disagree with this default, I think we generally agree that there should at least be an option to make extensible instances. Say public and private share one namespace. Say extensible instance X has private instance property 'foo'. Say a client of X tries to extend it with a public 'foo' property. What happens? Ah, I see. It's a fair point, but isn't this already a hazard that the language faces? Suppose I have two objects, extensible instance X with private instance property 'foo', and extensible object Y upon which I have defined a property 'foo' using Object.defineProperty, setting writable=false. If a client of Y tries to extend if with a public 'foo' property, then this would fail (throwing a TypeError in strict mode code). Would it not be acceptable for the attempted assignment to the private property of X to fail in a similar fashion? It seems that it is already the case that if a client wishes to associate data with a given object under any arbitrary name, then the only truly safe way to do so is through an external mapping such as a weak map / ephemeron table? Might it be reasonable to make private properties be regular properties on the object, with a new 'private' attribute, similar to the existing writable/configurable attributes? From the perspective of code outside of the associated class, an instance's private property would be non-readable, non-writable and non-configurable (likely also non-enumerable?), with any attempt to get, set, or delete the property failing in a similar manner to an existing writable/configurable attribute violation. It would be great to hear your thoughts on this. cheers, G. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Public/private namespaces in harmony classes proposal
On Jul 7, 2011, at 11:07 PM, Gavin Barraclough wrote: Ah, I see. It's a fair point, but isn't this already a hazard that the language faces? Not with private names. Suppose I have two objects, extensible instance X with private instance property 'foo', and extensible object Y upon which I have defined a property 'foo' using Object.defineProperty, setting writable=false. If a client of Y tries to extend if with a public 'foo' property, then this would fail (throwing a TypeError in strict mode code). Would it not be acceptable for the attempted assignment to the private property of X to fail in a similar fashion? No, that leaks the fact that there's a private-named object (if you also enumerate public names and do not find such a name; or enumerate property descriptors, etc.). It seems that it is already the case that if a client wishes to associate data with a given object under any arbitrary name, then the only truly safe way to do so is through an external mapping such as a weak map / ephemeron table? No, private names are in ES.next: http://wiki.ecmascript.org/doku.php?id=harmony:private_name_objects Might it be reasonable to make private properties be regular properties on the object, with a new 'private' attribute, similar to the existing writable/configurable attributes? From the perspective of code outside of the associated class, an instance's private property would be non-readable, non-writable and non-configurable (likely also non-enumerable?), with any attempt to get, set, or delete the property failing in a similar manner to an existing writable/configurable attribute violation. It would be great to hear your thoughts on this. See above -- private means you can't probe, let alone collide, for the private name from outside of the abstraction. Private name objects prove much more than a single-bit 'private' attribute -- they allow private, protected, friend, shared-secret, and public-but-guarnateed-unique names. /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Public/private namespaces in harmony classes proposal
On Jul 7, 2011, at 11:15 PM, Brendan Eich wrote: Suppose I have two objects, extensible instance X with private instance property 'foo', and extensible object Y upon which I have defined a property 'foo' using Object.defineProperty, setting writable=false. If a client of Y tries to extend if with a public 'foo' property, then this would fail (throwing a TypeError in strict mode code). Would it not be acceptable for the attempted assignment to the private property of X to fail in a similar fashion? No, that leaks the fact that there's a private-named object er, private property on the object. /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Type of property names, as seen by proxy traps
2011/7/7 David Herman dher...@mozilla.com 2011/7/6 Andreas Rossberg rossb...@google.com While putting together some test cases for Object.keys, I wondered: is it intended that property names are always passed to traps as strings? That is indeed the intent. Unless they are private name objects, right? I'm not sure. I briefly checked the private names proposal http://wiki.ecmascript.org/doku.php?id=harmony:private_name_objects and I think the detailed interaction with proxies still has to be fleshed out. The proposal does mention: All reflective operations that produce a property name, when reflecting on a private name, produce the name’s .public property instead of the name itself. Would the same hold for reflective operations that consume property names, such as handler traps? Cheers, Tom ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Minor type confusion in proxies proposal?
It seems like we need to make a change to Object.defineProperties, too (regardless of the other issue). With the current wording, it will not forward user attributes to the defineProperty trap. The most modular fix (that seems compatible with your proposal below) probably is to change the spec of defineProperties to go through defineProperty, instead of calling the internal [[DefineOwnProperty]] directly. /Andreas On 6 July 2011 14:55, Andreas Rossberg rossb...@google.com wrote: On 3 July 2011 13:29, Tom Van Cutsem tomvc...@gmail.com wrote: Comments? Looks good to me. I agree with Mark's comment that it should do a (shallow) copy of the attributes object, though. I guess the obvious point would be in Object.defineProperty, before passing it to DefineProxyProperty. /Andreas 2011/7/2 Andreas Rossberg rossb...@google.com Hi Tom. On 2 July 2011 13:50, Tom Van Cutsem tomvc...@gmail.com wrote: Hi Andreas, First, you're right about the typing issue: In ES5, for values of type Object, the signature for [[DefineOwnProperty]] would be: [[DefineOwnProperty]](P: a property name, Desc: an internal property descriptor, Throw: a boolean) On trapping proxies, that signature would need to change to: [[DefineOwnProperty]](P: a property name, Desc: an Object, Throw: a boolean) I don't think such a change is consistent. [[DefineOwnProperty]] is invoked in a number of places in the spec, and I think in many of them the type of the receiver is not distinguished and may well be a proxy, so a proxy may then receive both kinds of descriptors. Moreover, I think it would be a mistake to make the appropriate case distinction everywhere -- you really want the internal method to have the same signature in all cases. With that, I believe the strawman is otherwise internally consistent. In [[DefineOwnProperty]] step 5, what will be passed to the user-defined defineProperty trap is a proper Object, not an internal descriptor. I did clarify the note you referred to, to be more explicit in this regard. I don't see an alternative to changing the signature of [[DefineOwnProperty]]. It can't just receive an internal descriptor, as it doesn't preserve any non-standard attributes. How about simply bypassing [[DefineOwnProperty]] in Object.defineProperty for proxies, as I suggested in my reply to David? That seems to be the only place where additional objects can occur, or am I wrong? Cheers, /Andreas 2011/7/1 Andreas Rossberg rossb...@google.com On 1 July 2011 12:12, Andreas Rossberg rossb...@google.com wrote: I believe there is some type confusion in the proxy proposal spec wrt property descriptors and their reification into attributes objects. 1. In a note on the def of [[DefineOwnProperty]] for proxies, the proposal says: The Desc argument to this trap is a property descriptor object validated by ToPropertyDescriptor, except that it also retains any non-standard attributes present in the original property descriptor passed to Object.defineProperty. See the semantics of the modified Object.defineProperty built-in, below. That seems fishy, since according to ES5 8.10: Values of the Property Descriptor type are records composed of named fields where each field‘s name is an attribute name and its value is a corresponding attribute value as specified in 8.6.1. In particular, I take this to mean that property descriptors are not objects (but abstract records), and that there cannot be any fields whose name is not an attribute name. (In fact, in V8 we currently encode property descriptors using objects, but the encoding is different from the reified attributes object representation, and not quite compatible with the idea of adding arbitrary other fields.) I forgot to say: step 5 of the definition invokes the defineProperty trap of the handler passing Desc as the second argument. But the handler expects a reified attributes object. 2. In the modified definition of Object.defineProperty, the proposal says in step 4.c: Call the [[DefineOwnProperty]] internal method of O with arguments name, descObj, and true. This is passing descObj, which in fact is _not_ a descriptor, but its reification as an attributes object. /Andreas ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Minor type confusion in proxies proposal?
Likewise, invoking Object.getOwnPropertyDescriptor on a proxy does not return user attributes. That actually is explicitly noted in the semantics for [[GetOwnProperty]], but I'm not sure I see the rationale behind it. I would prefer a more coherent story with respect to proxies and user attributes on descriptor objects. That is, we should either support such attributes properly, i.e. have them consistently flow both ways (from clients to traps and vice versa). Or we do not support them at all, i.e. filter them out everywhere. /Andreas On 8 July 2011 10:59, Andreas Rossberg rossb...@google.com wrote: It seems like we need to make a change to Object.defineProperties, too (regardless of the other issue). With the current wording, it will not forward user attributes to the defineProperty trap. The most modular fix (that seems compatible with your proposal below) probably is to change the spec of defineProperties to go through defineProperty, instead of calling the internal [[DefineOwnProperty]] directly. /Andreas On 6 July 2011 14:55, Andreas Rossberg rossb...@google.com wrote: On 3 July 2011 13:29, Tom Van Cutsem tomvc...@gmail.com wrote: Comments? Looks good to me. I agree with Mark's comment that it should do a (shallow) copy of the attributes object, though. I guess the obvious point would be in Object.defineProperty, before passing it to DefineProxyProperty. /Andreas 2011/7/2 Andreas Rossberg rossb...@google.com Hi Tom. On 2 July 2011 13:50, Tom Van Cutsem tomvc...@gmail.com wrote: Hi Andreas, First, you're right about the typing issue: In ES5, for values of type Object, the signature for [[DefineOwnProperty]] would be: [[DefineOwnProperty]](P: a property name, Desc: an internal property descriptor, Throw: a boolean) On trapping proxies, that signature would need to change to: [[DefineOwnProperty]](P: a property name, Desc: an Object, Throw: a boolean) I don't think such a change is consistent. [[DefineOwnProperty]] is invoked in a number of places in the spec, and I think in many of them the type of the receiver is not distinguished and may well be a proxy, so a proxy may then receive both kinds of descriptors. Moreover, I think it would be a mistake to make the appropriate case distinction everywhere -- you really want the internal method to have the same signature in all cases. With that, I believe the strawman is otherwise internally consistent. In [[DefineOwnProperty]] step 5, what will be passed to the user-defined defineProperty trap is a proper Object, not an internal descriptor. I did clarify the note you referred to, to be more explicit in this regard. I don't see an alternative to changing the signature of [[DefineOwnProperty]]. It can't just receive an internal descriptor, as it doesn't preserve any non-standard attributes. How about simply bypassing [[DefineOwnProperty]] in Object.defineProperty for proxies, as I suggested in my reply to David? That seems to be the only place where additional objects can occur, or am I wrong? Cheers, /Andreas 2011/7/1 Andreas Rossberg rossb...@google.com On 1 July 2011 12:12, Andreas Rossberg rossb...@google.com wrote: I believe there is some type confusion in the proxy proposal spec wrt property descriptors and their reification into attributes objects. 1. In a note on the def of [[DefineOwnProperty]] for proxies, the proposal says: The Desc argument to this trap is a property descriptor object validated by ToPropertyDescriptor, except that it also retains any non-standard attributes present in the original property descriptor passed to Object.defineProperty. See the semantics of the modified Object.defineProperty built-in, below. That seems fishy, since according to ES5 8.10: Values of the Property Descriptor type are records composed of named fields where each field‘s name is an attribute name and its value is a corresponding attribute value as specified in 8.6.1. In particular, I take this to mean that property descriptors are not objects (but abstract records), and that there cannot be any fields whose name is not an attribute name. (In fact, in V8 we currently encode property descriptors using objects, but the encoding is different from the reified attributes object representation, and not quite compatible with the idea of adding arbitrary other fields.) I forgot to say: step 5 of the definition invokes the defineProperty trap of the handler passing Desc as the second argument. But the handler expects a reified attributes object. 2. In the modified definition of Object.defineProperty, the proposal says in step 4.c: Call the [[DefineOwnProperty]] internal method of O with arguments name, descObj, and true. This is passing descObj, which in fact is _not_ a descriptor, but its reification as an attributes object. /Andreas
Re: Public/private namespaces in harmony classes proposal
On Fri, Jul 8, 2011 at 3:15 AM, Brendan Eich bren...@mozilla.com wrote: On Jul 7, 2011, at 11:07 PM, Gavin Barraclough wrote: Ah, I see. It's a fair point, but isn't this already a hazard that the language faces? Not with private names. There's also something that was probably discussed but never got to this list: Private instance properties were considered to be accessible by all instances of the class. That's a new concept in ES. Were there arguments for keeping instance private properties private to the instance? Juan ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Public/private namespaces in harmony classes proposal
On Fri, Jul 8, 2011 at 6:36 AM, Juan Ignacio Dopazo dopazo.j...@gmail.comwrote: On Fri, Jul 8, 2011 at 3:15 AM, Brendan Eich bren...@mozilla.com wrote: On Jul 7, 2011, at 11:07 PM, Gavin Barraclough wrote: Ah, I see. It's a fair point, but isn't this already a hazard that the language faces? Not with private names. There's also something that was probably discussed but never got to this list: Private instance properties were considered to be accessible by all instances of the class. That's a new concept in ES. Were there arguments for keeping instance private properties private to the instance? Yes. That's done quite well by lexical capture of the constructor lexical context by per-instance methods: class Point { constructor(x, y) { public getX() { return x; } ... } } which basically sugars the objects-as-closure pattern. If you don't mind the allocation cost, this is superior in almost all ways to the conventional prototype/this-based pattern. private is needed when combining inherited methods with encapsulation. Since inherited methods may apply to any instance of the class and are not automagically bound to any one instance, their access to encapsulated state cannot be any finer than per-class. Juan ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss -- Cheers, --MarkM ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Type of property names, as seen by proxy traps
I'm not sure. I briefly checked the private names proposal http://wiki.ecmascript.org/doku.php?id=harmony:private_name_objects and I think the detailed interaction with proxies still has to be fleshed out. Sure, I'll be happy to work with you on this. The proposal does mention: All reflective operations that produce a property name, when reflecting on a private name, produce the name’s .public property instead of the name itself. Would the same hold for reflective operations that consume property names, such as handler traps? No, they would require the private name object. The idea here is that you need a reference to the private name to get access to its property. So you can't do any proxy operations on a private property if you don't have the private name object. But the proxy traps do not automatically hand out that reference to a handler trap, in case the trap didn't already have a reference to it (which would constitute a leak). Instead, it hands them the corresponding public key. This way, *if* the trap has a reference to the private key, it can identify which private name is being accessed. Otherwise, the trap can't conclude anything more than operation X was requested on *some* private name. Dave ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
JS control-structure abstractions, using tailnest flattening and tailcall optimization
Dear all, we have seen examples of how to define control-structure abstractions via block-lambdas (and Smalltalk blocks), including non-local returns out of user-defined loops. I'd like to provide an example of how to do something like this with JS, using only the proposed syntactic sugar for flat syntactic tailnests and the upcoming support for flat runtime tailcall stacks (for callback chains, the two complement each other). Building up from small structures, the example aims to implement a variant of yield. With conventional blocks, this requires the upcoming generators language extension. Block-lambdas do not help here, I think, because yield can suspend and resume at statement level, and even the slightly unusual sequel feature does only support non-local return, no resume (calling a sequel returns to a lexically scoped elsewhere, not to the caller). If JS had call/cc or delimited continuations, we could implement yield on top of that, but that isn't likely. If we were to rewrite our code in continuation passing style (cps), we could implement call/cc, but even in languages with lightweight function syntax, that is not considered very readable. There are modular variants of continuation passing style that can be made to look very similar to normal code, but these become unreadable in JS syntax. Cps without tailcall optimization also quickly overflows the stack. Which is where tailnests (readability) and tailcalls (efficiency/ useability) come in, addressing the issues that cps in current JS leads to overflows in syntactic and runtime stack nesting. First, the programming pattern: in plain cps, we'd have nested callback chains feeding intermediate results into the rest of the callback chain (assuming currying for the callback parameter): operation(..)( function(result) { ..operations..} ) With paren-free right-associative calls (f @ arg) and brace-free definitions (function(arg) = expr) for expression functions, this becomes: operation(..) @ function(result) = ..operations.. Tailcall optimization guarantees that the callbacks will not overflow the runtime stack, and tailnest flattening keeps the level of syntactic nesting independent of the chain length. In modular cps, instead of every operation taking a callback parameter, every operation returns its result in an object implementing a single method 'then' (the ideas are standard, the translation to JS is not). Calling 'then' with a callback feeds a value/intermediate result to the callback. operation(..).then @ function(result) = ..operations.. (which can be read as a clumsy way to write let result - operation in operations, but the .then allows us to give different meanings to the variable binding, eg., it could mean foreach result from operation, do operations, or it could store the continuation and jump elsewhere, as yield) The most basic operation is 'value', which just wraps a value into a then-able: function value(val) = { then : function(cont) = cont(val) } Since statements represented as then-ables are objects, conditional statements are just conditional expressions: cond ? t : e or, if the condition involves then-able statements, too: cond.then @ function( c ) = c ? t : e which we can either use directly, or wrap in a function (the then/else-branch arguments are themselves wrapped to delay their evaluation): function if_(cond) = function(ft) = function(fe) = cond.then @ function( c ) = c ? ft() : fe() Hence, a while-loop with then-able condition and delayed body: function while_(cond) = function(body) = if_(cond) (function() = body().then @ function() = while_(cond)(body) ) (function() = value( null ) ) You can find these examples online [1], so to limit this email, I'll jump right to the interesting bit, which is implementing yield: yield_(val) produces a then-able that behaves a bit unusually: instead of passing a value to continuations passed via .then(), it swallows all such continuations, storing them for later use (in plain cps, we'd have the whole continuation at hand; in this modular cps variation, we have to collect nested continuations). To get at the yielded value, we can call yield_(val).value(cont), and to restart the generator, we can call yield_(val).next(), which applies the internally stored continuations to val. In code: function yield_(val) = { then: stack(val) }; function stack(val) = function(c1) = { then: function(c2) = stack(val)(comp_then(c2,c1)) , value: function(cont) = cont(val) , next: function() = c1(val) }; function comp_then(c2,c1) = function(val) = c1(val).then(c2); Again, the commented code and a couple of example generators can be found online [1]. If you use the desugaring page, you can see why this style isn't popular without syntactic sugar. But the functionality is plain JS, no semantic changes! The code
Re: Minor type confusion in proxies proposal?
2011/7/8 Andreas Rossberg rossb...@google.com It seems like we need to make a change to Object.defineProperties, too (regardless of the other issue). With the current wording, it will not forward user attributes to the defineProperty trap. The most modular fix (that seems compatible with your proposal below) probably is to change the spec of defineProperties to go through defineProperty, instead of calling the internal [[DefineOwnProperty]] directly. I agree. Noted. /Andreas On 6 July 2011 14:55, Andreas Rossberg rossb...@google.com wrote: On 3 July 2011 13:29, Tom Van Cutsem tomvc...@gmail.com wrote: Comments? Looks good to me. I agree with Mark's comment that it should do a (shallow) copy of the attributes object, though. I guess the obvious point would be in Object.defineProperty, before passing it to DefineProxyProperty. /Andreas 2011/7/2 Andreas Rossberg rossb...@google.com Hi Tom. On 2 July 2011 13:50, Tom Van Cutsem tomvc...@gmail.com wrote: Hi Andreas, First, you're right about the typing issue: In ES5, for values of type Object, the signature for [[DefineOwnProperty]] would be: [[DefineOwnProperty]](P: a property name, Desc: an internal property descriptor, Throw: a boolean) On trapping proxies, that signature would need to change to: [[DefineOwnProperty]](P: a property name, Desc: an Object, Throw: a boolean) I don't think such a change is consistent. [[DefineOwnProperty]] is invoked in a number of places in the spec, and I think in many of them the type of the receiver is not distinguished and may well be a proxy, so a proxy may then receive both kinds of descriptors. Moreover, I think it would be a mistake to make the appropriate case distinction everywhere -- you really want the internal method to have the same signature in all cases. With that, I believe the strawman is otherwise internally consistent. In [[DefineOwnProperty]] step 5, what will be passed to the user-defined defineProperty trap is a proper Object, not an internal descriptor. I did clarify the note you referred to, to be more explicit in this regard. I don't see an alternative to changing the signature of [[DefineOwnProperty]]. It can't just receive an internal descriptor, as it doesn't preserve any non-standard attributes. How about simply bypassing [[DefineOwnProperty]] in Object.defineProperty for proxies, as I suggested in my reply to David? That seems to be the only place where additional objects can occur, or am I wrong? Cheers, /Andreas 2011/7/1 Andreas Rossberg rossb...@google.com On 1 July 2011 12:12, Andreas Rossberg rossb...@google.com wrote: I believe there is some type confusion in the proxy proposal spec wrt property descriptors and their reification into attributes objects. 1. In a note on the def of [[DefineOwnProperty]] for proxies, the proposal says: The Desc argument to this trap is a property descriptor object validated by ToPropertyDescriptor, except that it also retains any non-standard attributes present in the original property descriptor passed to Object.defineProperty. See the semantics of the modified Object.defineProperty built-in, below. That seems fishy, since according to ES5 8.10: Values of the Property Descriptor type are records composed of named fields where each field‘s name is an attribute name and its value is a corresponding attribute value as specified in 8.6.1. In particular, I take this to mean that property descriptors are not objects (but abstract records), and that there cannot be any fields whose name is not an attribute name. (In fact, in V8 we currently encode property descriptors using objects, but the encoding is different from the reified attributes object representation, and not quite compatible with the idea of adding arbitrary other fields.) I forgot to say: step 5 of the definition invokes the defineProperty trap of the handler passing Desc as the second argument. But the handler expects a reified attributes object. 2. In the modified definition of Object.defineProperty, the proposal says in step 4.c: Call the [[DefineOwnProperty]] internal method of O with arguments name, descObj, and true. This is passing descObj, which in fact is _not_ a descriptor, but its reification as an attributes object. /Andreas ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Minor type confusion in proxies proposal?
2011/7/8 Andreas Rossberg rossb...@google.com Likewise, invoking Object.getOwnPropertyDescriptor on a proxy does not return user attributes. That actually is explicitly noted in the semantics for [[GetOwnProperty]], but I'm not sure I see the rationale behind it. I would prefer a more coherent story with respect to proxies and user attributes on descriptor objects. That is, we should either support such attributes properly, i.e. have them consistently flow both ways (from clients to traps and vice versa). Or we do not support them at all, i.e. filter them out everywhere. I believe the note at [[GetOwnProperty]] predates our thoughts on allowing user-defined attributes. So yes, we should be consistent and perform the same type of normalization of property descriptors as for defineProperty. /Andreas On 8 July 2011 10:59, Andreas Rossberg rossb...@google.com wrote: It seems like we need to make a change to Object.defineProperties, too (regardless of the other issue). With the current wording, it will not forward user attributes to the defineProperty trap. The most modular fix (that seems compatible with your proposal below) probably is to change the spec of defineProperties to go through defineProperty, instead of calling the internal [[DefineOwnProperty]] directly. /Andreas On 6 July 2011 14:55, Andreas Rossberg rossb...@google.com wrote: On 3 July 2011 13:29, Tom Van Cutsem tomvc...@gmail.com wrote: Comments? Looks good to me. I agree with Mark's comment that it should do a (shallow) copy of the attributes object, though. I guess the obvious point would be in Object.defineProperty, before passing it to DefineProxyProperty. /Andreas 2011/7/2 Andreas Rossberg rossb...@google.com Hi Tom. On 2 July 2011 13:50, Tom Van Cutsem tomvc...@gmail.com wrote: Hi Andreas, First, you're right about the typing issue: In ES5, for values of type Object, the signature for [[DefineOwnProperty]] would be: [[DefineOwnProperty]](P: a property name, Desc: an internal property descriptor, Throw: a boolean) On trapping proxies, that signature would need to change to: [[DefineOwnProperty]](P: a property name, Desc: an Object, Throw: a boolean) I don't think such a change is consistent. [[DefineOwnProperty]] is invoked in a number of places in the spec, and I think in many of them the type of the receiver is not distinguished and may well be a proxy, so a proxy may then receive both kinds of descriptors. Moreover, I think it would be a mistake to make the appropriate case distinction everywhere -- you really want the internal method to have the same signature in all cases. With that, I believe the strawman is otherwise internally consistent. In [[DefineOwnProperty]] step 5, what will be passed to the user-defined defineProperty trap is a proper Object, not an internal descriptor. I did clarify the note you referred to, to be more explicit in this regard. I don't see an alternative to changing the signature of [[DefineOwnProperty]]. It can't just receive an internal descriptor, as it doesn't preserve any non-standard attributes. How about simply bypassing [[DefineOwnProperty]] in Object.defineProperty for proxies, as I suggested in my reply to David? That seems to be the only place where additional objects can occur, or am I wrong? Cheers, /Andreas 2011/7/1 Andreas Rossberg rossb...@google.com On 1 July 2011 12:12, Andreas Rossberg rossb...@google.com wrote: I believe there is some type confusion in the proxy proposal spec wrt property descriptors and their reification into attributes objects. 1. In a note on the def of [[DefineOwnProperty]] for proxies, the proposal says: The Desc argument to this trap is a property descriptor object validated by ToPropertyDescriptor, except that it also retains any non-standard attributes present in the original property descriptor passed to Object.defineProperty. See the semantics of the modified Object.defineProperty built-in, below. That seems fishy, since according to ES5 8.10: Values of the Property Descriptor type are records composed of named fields where each field‘s name is an attribute name and its value is a corresponding attribute value as specified in 8.6.1. In particular, I take this to mean that property descriptors are not objects (but abstract records), and that there cannot be any fields whose name is not an attribute name. (In fact, in V8 we currently encode property descriptors using objects, but the encoding is different from the reified attributes object representation, and not quite compatible with the idea of adding arbitrary other fields.) I forgot to say: step 5 of the definition invokes the defineProperty trap of the handler passing Desc as the second argument. But the
Re: Public/private namespaces in harmony classes proposal
On Jul 8, 2011, at 6:36 AM, Juan Ignacio Dopazo wrote: On Fri, Jul 8, 2011 at 3:15 AM, Brendan Eich bren...@mozilla.com wrote: On Jul 7, 2011, at 11:07 PM, Gavin Barraclough wrote: Ah, I see. It's a fair point, but isn't this already a hazard that the language faces? Not with private names. There's also something that was probably discussed but never got to this list: Private instance properties were considered to be accessible by all instances of the class. That's a new concept in ES. Were there arguments for keeping instance private properties private to the instance? Yes, on this list, going back to the Harmony Oslo July 2008 meeting, with followup at the Kona Nov. 2008 meeting. Mark may have notes from the more recent sub-group of TC39ers who championed classes. My recollection is that we chose class-private for both usability and private-name equivalence, but we still have open issues on how one accesses private foo in both |this| and |other| for a dyadic operator method, e.g. /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Public/private namespaces in harmony classes proposal
On Jul 8, 2011, at 7:43 AM, Juan Ignacio Dopazo wrote: My first thought was: why not just statically replace this.x, with private x, with this[xPrivateName] and forget about accessing private properties of other objects? That would still leave the problem of closures inside methods, though... If xPrivateName is a private name object, then there's no reason to forget about accessing private properties of other objects. Both this[xPrivateName] and other[xPrivateName] work as you would expect. /be___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Type of property names, as seen by proxy traps
On Jul 8, 2011, at 7:17 AM, David Herman wrote: The proposal does mention: All reflective operations that produce a property name, when reflecting on a private name, produce the name’s .public property instead of the name itself. Would the same hold for reflective operations that consume property names, such as handler traps? No, they would require the private name object. I don't think that's what Tom was asking about, though. The proposal may simply be unclear in using produce instead of consume since the proxy mechanism does not produce private names in any generative sense when one writes p[q] for proxy p and private name q. Rather, the VM substitutes q.public for q when calling p's handler's relevant trap (getOwnPropertyDescriptor, get, ...). So there's no leak, as you note, and the owner of q is free to share it with trap implementations that should have access to it, so they can compare name == q.public, memoize in q.public in a weak map, etc. /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Type of property names, as seen by proxy traps
Sorry, yes. Too early in the morning for me. :) Indeed, handler traps are exactly the place where the system *produces* names and hands them to handler traps which consume them, and that's where it must produce a public key rather than a private name object. Dave On Jul 8, 2011, at 8:20 AM, Brendan Eich wrote: On Jul 8, 2011, at 7:17 AM, David Herman wrote: The proposal does mention: All reflective operations that produce a property name, when reflecting on a private name, produce the name’s .public property instead of the name itself. Would the same hold for reflective operations that consume property names, such as handler traps? No, they would require the private name object. I don't think that's what Tom was asking about, though. The proposal may simply be unclear in using produce instead of consume since the proxy mechanism does not produce private names in any generative sense when one writes p[q] for proxy p and private name q. Rather, the VM substitutes q.public for q when calling p's handler's relevant trap (getOwnPropertyDescriptor, get, ...). So there's no leak, as you note, and the owner of q is free to share it with trap implementations that should have access to it, so they can compare name == q.public, memoize in q.public in a weak map, etc. /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Type of property names, as seen by proxy traps
And just to be clear, I meant produce in the sense of producer/consumer relationship on the trap functions, not in the generative sense. Dave On Jul 8, 2011, at 8:40 AM, David Herman wrote: Sorry, yes. Too early in the morning for me. :) Indeed, handler traps are exactly the place where the system *produces* names and hands them to handler traps which consume them, and that's where it must produce a public key rather than a private name object. Dave On Jul 8, 2011, at 8:20 AM, Brendan Eich wrote: On Jul 8, 2011, at 7:17 AM, David Herman wrote: The proposal does mention: All reflective operations that produce a property name, when reflecting on a private name, produce the name’s .public property instead of the name itself. Would the same hold for reflective operations that consume property names, such as handler traps? No, they would require the private name object. I don't think that's what Tom was asking about, though. The proposal may simply be unclear in using produce instead of consume since the proxy mechanism does not produce private names in any generative sense when one writes p[q] for proxy p and private name q. Rather, the VM substitutes q.public for q when calling p's handler's relevant trap (getOwnPropertyDescriptor, get, ...). So there's no leak, as you note, and the owner of q is free to share it with trap implementations that should have access to it, so they can compare name == q.public, memoize in q.public in a weak map, etc. /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Public/private namespaces in harmony classes proposal
On Fri, Jul 8, 2011 at 12:15 PM, Brendan Eich bren...@mozilla.com wrote: On Jul 8, 2011, at 7:43 AM, Juan Ignacio Dopazo wrote: My first thought was: why not just statically replace this.x, with private x, with this[xPrivateName] and forget about accessing private properties of other objects? That would still leave the problem of closures inside methods, though... If xPrivateName is a private name object, then there's no reason to forget about accessing private properties of other objects. Both this[xPrivateName] and other[xPrivateName] work as you would expect. /be You are very much right. What are the open issues with privates in classes then? Juan ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: JS control-structure abstractions, using tailnest flattening and tailcall optimization
On Jul 8, 2011, at 7:34 AM, Claus Reinke wrote: Tailcall optimization guarantees that the callbacks will not overflow the runtime stack, and tailnest flattening keeps the level of syntactic nesting independent of the chain length. ES.next has generators and proper tail calls already. I'm hard-pressed to call the syntax extensions you propose 'more readable than what can be done with generators and libraries such as taskjs (https://github.com/dherman/taskjs). I have to say @ and expression-forms seem less readable in general. /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Public/private namespaces in harmony classes proposal
On Fri, Jul 8, 2011 at 1:52 PM, Brendan Eich bren...@mozilla.com wrote: On Jul 8, 2011, at 8:45 AM, Juan Ignacio Dopazo wrote: You are very much right. What are the open issues with privates in classes then? The wiki lists some. Here are a few from memory: * Syntax to use instead of private(this), private(other). The straw private(foo) syntax is too verbose and it wrongly suggests that a private data record is a distinct object. Are we ready to claim @ as prefix and infix operator (with restriction against LineTerminator on its left)? Why do privates need special syntax? Isn't the point to just use them with this.privateWhatever? * Private prototype methods and other properties may be useful, especially methods. One can wrap a closure around the class and put private helpers there, of course, but with private syntax in the proposal, why stop short? +1! If we do support private prototype properties, then what are the semantics? Private name objects, as recently noted, have their .public counterparts passed as name parameters to proxy traps, so something about private prototype properties may be observable: class Victim prototypes Proxy { private protoMethod() { privat(super).protoMethod(); } } Isn't that only an issue of protected vs private? Juan ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Public/private namespaces in harmony classes proposal
On Jul 8, 2011, at 10:20 AM, Juan Ignacio Dopazo wrote: On Fri, Jul 8, 2011 at 1:52 PM, Brendan Eich bren...@mozilla.com wrote: On Jul 8, 2011, at 8:45 AM, Juan Ignacio Dopazo wrote: You are very much right. What are the open issues with privates in classes then? The wiki lists some. Here are a few from memory: * Syntax to use instead of private(this), private(other). The straw private(foo) syntax is too verbose and it wrongly suggests that a private data record is a distinct object. Are we ready to claim @ as prefix and infix operator (with restriction against LineTerminator on its left)? Why do privates need special syntax? Isn't the point to just use them with this.privateWhatever? No, because of the other.x problem: class Point { constructor(x, y) {...} equals(other) { return this.x is other.x this.y is other.y; } ... } We cannot know how to ask for private-x from other without special syntax of some kind, either at the access point or as a binding declaration affecting all .x (and x: in object literals). If we do support private prototype properties, then what are the semantics? Private name objects, as recently noted, have their .public counterparts passed as name parameters to proxy traps, so something about private prototype properties may be observable: class Victim prototypes Proxy { private protoMethod() { privat(super).protoMethod(); } } Isn't that only an issue of protected vs private? Sorry, that may be a bad example. Consider the Point example above, where other is a proxy and we use @ for private access: class Point { constructor(x, y) {...} equals(other) { return @x is other@x @y is other@y; } ... } (The unary prefix form @foo is short for binary this@foo.) The spec has to say what the proxy denoted by other sees as the name parameter when its handler's relevant trap is called. /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
using Private name objects for declarative property definition.
The current Harmony classes proposal http://wiki.ecmascript.org/doku.php?id=harmony:classes includes the concept of private instance members and syntax for defining them. While it presents a syntax for accessing them (eg, private(foo).bar accesses the private 'bar' member of the object that is the value of foo) there does not yet appear to be consensus acceptance of this access syntax. There also appears to be a number of still unresolved semantic issues WRT such private instance members. As an alternative, the classes proposal says that a pattern composing private names with classes can satisfy the requirements for private instance members. However, so far, we have no concrete proposals on what these patterns might be. This is a first cut at proposing such patterns. The current versions of the private names proposal http://wiki.ecmascript.org/doku.php?id=harmony:private_name_objects simply exposes a constructor for creating unique values can be be used as property keys: const key = Name.create(); Those values can be used to define and access object properties: let obj = {}; print(obj.key); // undefined obj[key]=some private state; //create a property with a private name key print(obj.key);// still undefined print(obj[key]); //undefined print(obj[key]); // some private state In the above example the property created by using the private name value is essentially an instance private member of obj. In general, various levels of visibility (instance private, class private, friends, module private, etc.) can be achieved simply by using lexical scoping to control access to the variable that contain private key values. For example: function ObjWithInstancePrivateMember(secret) { const s = Name.create(); this[s]= secret; this.method = function () { return doSomething(this[s]); } } const priv = Name.create(); function ObjWithClassPrivateMember(secret) { const s = Name.create(); this[priv]= secret; this.method = function (another) { return doSomethingTogether(this[priv], another[priv]); } } Note that [ ] notation with an expression that evaluates to a private name value must be used to access such private members. There have been various proposals such as http://wiki.ecmascript.org/doku.php?id=strawman:names and http://wiki.ecmascript.org/doku.php?id=strawman:private_names to allow dotted property access to be used with private name keys. However, these either had technical problems or their semantics were rejected by community discussion on this list. The issues raised in response to those proposals are likely to recur for any private member access syntax that uses dot notation. A weakness with using private names object to define private members is that currently there is no way to declaratively define objects with such members. Consider the following definition of a Point abstraction using extended object literals and a simple naming convention to identify private members: const Point = { //private members __x: 0, __y: 0, __validate(x,y) { return typeof x == 'number' typeof y = 'number'}, //public members new(x,y) { if (!this.__validate(x,y)) throw invalid; return this | { __x: x, __y: y } }; add(anotherPoint) { return this.new(this.__x+another.__x, this.__y+another.__y) } } using private name objects this would have to be written as: const __x=Name.create(); const __y=Name.create(); const __validate=Name.create(); const Point = { //public members new(x,y) { if (!this[__validate](x,y)) throw invalid; let obj = this | { }; obj[__x] = x; obj[__y] = y; return obj; }; add(anotherPoint) { return this.new(this[__x]+another[__x], this[__y]+another[__y]) } } //private members for Point Point[__x] = 0; Point[__y] = 0,; Point[ __validate] = function (x,y) { return typeof x == 'number' tyupeof y = 'number'}; The biggest issue with this rewrite is it loose the declarative definition and replaces it with an hybrid that has some declarative parts and some imperative parts. This breaks the logical encapsulation of the definition of the Point abstraction making it harder to be understand and manipulated by both humans and mechanical tools. The reason this is necessary is that object literal notation currently has no way to express that a definition of a property uses a name that needs be interpreted as a reference to a lexical variable whose value is the actual private name object. Fixes these requires an additional extension to object literals. One possibility, is to use a prefix keyword to each property definition that uses a private key value. For example: const __x=Name.create(); const
Re: using Private name objects for declarative property definition.
On 8 July 2011 21:16, Allen Wirfs-Brock al...@wirfs-brock.com wrote: The current versions of the private names proposal http://wiki.ecmascript.org/doku.php?id=harmony:private_name_objects simply exposes a constructor for creating unique values can be be used as property keys: Of the several private names proposals around, I find this one preferable. It is clean and simple, and provides the functionality needed in an orthogonal manner. It seems worth exploring how well this works in practice before we settle on something more complicated. One minor suggestion I'd have is to treat names as a proper new primitive type, i.e. typeof key == name, not object. That way, it can be defined much more cleanly what a name is, where its use is legal (as opposed to proper objects), and where it maybe enjoys special treatment. Another alternative that avoids using the 'private' prefix is to allow the property name in a property definition to be enclosed with brackets: const __x=Name.create(); const __y=Name.create(); const __validate=Name.create(); Point = { //private members [__x]: 0, [ __y]: 0, [__validate](x,y) { return typeof x == 'number' typeof y = 'number'}, //public members new(x,y) { if (!this[__validate](x,y)) throw invalid; return this | { [__x]: x, [__y]: y } }; add(anotherPoint) { return this.new(this[__x]+another[__x], this[__y]+another[__y]) } } I like this notation most, because it can be generalised in a consistent manner beyond the special case of private names: there is no reason that the bit in brackets is just an identifier, we could allow arbitrary expressions. So the notation would be the proper dual to bracket access notation. From a symmetry and expressiveness perspective, this is very appealing. Notation-wise, I think people would get used to using brackets. I see no good reason to introduce yet another projection syntax, like @. Whether additional sugar is worthwhile -- e.g. private declarations -- remains to be explored. (To be honest, I haven't quite understood yet in what sense such sugar would really be more declarative. Sure, it is convenient and perhaps more readable. But being declarative is a semantic property, and cannot be achieved by simple syntax tweaks.) /Andreas ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: using Private name objects for declarative property definition.
On Jul 8, 2011, at 12:16 PM, Allen Wirfs-Brock wrote: The current Harmony classes proposal http://wiki.ecmascript.org/doku.php?id=harmony:classes includes the concept of private instance members and syntax for defining them. While it presents a syntax for accessing them (eg, private(foo).bar accesses the private 'bar' member of the object that is the value of foo) there does not yet appear to be consensus acceptance of this access syntax. Oh, quite the opposite -- everyone on TC39 with whom I've spoken agrees that private(this) syntax is straw that must be burned up. We need new syntax, or else we need to do at least what Dave proposed in minimal classes: defer private syntax, let programmers use private name objects and explicit [] indexing. I do think we can say a few more things about private in class that may not be in the requirements on the wiki: On the level of rationale for why class- and not instance-private, Juan Ignacio Dopazo in private correspondence made a good observation, shown by his example: class MyClass { private foo() {} bar() { var self = this; setTimeout(function () { self.foo(); }, 0); } } Because |this| is not lexical, instance- rather than class-private access that requires this. to the left of the private variable reference does not work unless you use the closure pattern explicitly and abjure |this|. The straw private(foo) syntax doesn't help if the goal is instance privacy, since the inner function has no idea (runtime, never mind compile-time) how to enforce to which particular instance self must refer. Requiring .bind(this) after the function expression passed to setTimeout can be used to work around such a hypothetical, mandatory this.-prefix instance-private restriction, but that's onerous and it can be too costly. Another observation about any class-private scheme we might consider in the current context: private instance variables in many ways (notably not for Object.freeze) act like properties, and the syntax mooted so far casts them in that light. Even if the ES5 reflective APIs, such as Object.getOwnPropertyNames, rightly skip privates on a class instance, proxies may raise the question: how does a private variable name reflect as a property name? class Point { constructor(x, y) { private x = x, y = y; } equals(other) { return private(this).x is private(other).x private(this).y is private(other).y; } ... } We cannot know how to ask for private-x from other without special syntax of some kind, either at the access point or as a binding declaration affecting all .x (and x: in object literals). So here I use the proposal's straw private(foo) syntax. Could other be a proxy that somehow has a private data record? Could other denote a class instance whose [[Prototype]] is a proxy? I claim we do not want private(foo) by itself, no .x after, to reify as an object, but if it did, then it seems to me it could be used with a proxy to reflect on private variable names. There are certainly several choices here, but the one I currently favor as simplest, which creates no new, ad-hoc concepts in the language, is that class-private instance variables are properties named by private name objects. Per the requirements for private in the classes proposal, this means Object.freeze does not freeze private-named properties, or at least does not make private-named data properties non-writable. Perhaps Object.preventExtensions should not restrict private-named properties from being added to the object. This seems strange and wrong at first, but if freeze does not affect private-named properties, I'm not sure there is a defensive consistency threat from attackers decorating frozen objects with ad-hoc properties named by private name objects that only the attacker could create or access, which cannot collide with the class's private names. Perhaps this is uncontroversial. I hope so, but I don't assume it is, and I suspect people who worked on the classes proposal may disagree. Cc'ing Mark in particular, since I'm just writing down some preliminary thoughts and intermediate conclusions here, and I could be way off base. Ok, back to your good point about wanting private names to be usable in object initialisers: Each of these approaches seem plausible. For a side-by-side comparison of the above example using the alternatives see http://wiki.ecmascript.org/lib/exe/fetch.php?id=harmony%3Aprivate_name_objectscache=cachemedia=harmony:private-name-alternatives.pdf . I'm interested in feedback on the alternatives. The first thing I'd say is that, whatever the syntax (private prefix, [] around property name, or @ prefix), it seems too verbose to require boilerplate of const __x = Name.create(), etc. before the object literal. Wouldn't it be better for the prefix, brackets or sigil to by itself define a private name and use it, also putting it in scope for the
Re: using Private name objects for declarative property definition.
On Jul 8, 2011, at 2:43 PM, Andreas Rossberg wrote: One minor suggestion I'd have is to treat names as a proper new primitive type, i.e. typeof key == name, not object. That way, it can be defined much more cleanly what a name is, where its use is legal (as opposed to proper objects), and where it maybe enjoys special treatment. We went back and forth on this. I believe the rationale is in the wiki (but perhaps in one of the strawman:*name* pages). There are a couple of reasons: 1. We want private name objects to be usable as keys in WeakMaps. Clearly we could extend WeakMaps to have either object (but not null) or name typeof-type keys, but that complexity is not warranted yet. 2. Private name objects are deeply frozen and behave like value types (since they have no copy semantics and you can only generate fresh ones). Thus they are typeof-type object but clearly distinct from string-equated property names that JS has sported so far. In other words, we don't gain any distinctiveness, or make any particular claims about private name objects that could not be made about other (deeply-frozen, generated-only, say by Object.create or Proxy.create in a distinguished factory function) kinds of objects, via a new typeof-type. In light of these points, making private names be objects in both the as-proposed WeakMap sense, and the typeof-result sense, seems best. So, while we have added null as a new typeof result string, and we have considered adding other novel typeof results, and we believe that we can add new typeof results with good enough reason (thanks to IE adding some non-standard ones long ago, and programmers tending to write non-exhaustive switch and if-else cond structures to handle only certain well-known typeof cases), in the case of private name objects, we don't think we have good enough reason to add a typeof name -- and then to complicate WeakMap. Point = { //private members [__x]: 0, [ __y]: 0, [__validate](x,y) { return typeof x == 'number' typeof y = 'number'}, //public members new(x,y) { if (!this[__validate](x,y)) throw invalid; return this | { [__x]: x, [__y]: y } }; add(anotherPoint) { return this.new(this[__x]+another[__x], this[__y]+another[__y]) } } I like this notation most, because it can be generalised in a consistent manner beyond the special case of private names: there is no reason that the bit in brackets is just an identifier, we could allow arbitrary expressions. Then the shape of the object is not static. Perhaps this is worth the costs to implementations and other analyzers (static program analysis, human readers). We should discuss a bit more first, as I just wrote in reply to Allen. So the notation would be the proper dual to bracket access notation. From a symmetry and expressiveness perspective, this is very appealing. It does help avoid eval abusage, on the upside. Proceeding bottom-up, with orthogonal gap-filling primitives that compose well, is our preferred way for Harmony. Private name objects without new syntax, requiring bracket-indexing, won in part by filling a gap without jumping to premature syntax with novel binding semantics (see below). Here, with obj = { [expr]: value } as the way to compute a property name in an object initialiser (I must not write object literal any longer), we are proceeding up another small and separate hill. But, is this the right design for object initialisers (which the normative grammar does call ObjectLiterals)? Notation-wise, I think people would get used to using brackets. I see no good reason to introduce yet another projection syntax, like @. Agreed that unless we use @ well for both property names in initialisers and private property access, and in particular if we stick with bracketing for access, then square brackets win for property naming too -- but there's still the loss-of-static-shape issue. Whether additional sugar is worthwhile -- e.g. private declarations -- remains to be explored. (To be honest, I haven't quite understood yet in what sense such sugar would really be more declarative. Sure, it is convenient and perhaps more readable. But being declarative is a semantic property, and cannot be achieved by simple syntax tweaks.) Good point! The original name declaration via private x that Dave championed was definitely semantic: it created a new static lookup hierarchy, lexical but for names after . in expressions and before : in property assignments in object literals. This was, as Allen noted, controversial and enough respected folks on es-discuss (I recall Andrew Dupont in particular) and in TC39 reacted negatively that we separated and deferred it. I do not know how to revive it productively. /be___ es-discuss mailing list es-discuss@mozilla.org
Design principles for extending ES object abstractions
On Jul 8, 2011, at 2:58 PM, Brendan Eich wrote on the thread using Private name objects for declarative property definition. : But whatever the class syntax, and the disposition of private in class and even classes in ES.next, I agree we should expect private declarative and expression forms to work the same in object initialisers and in classes. It would be good to get everyone buying into this private-means-property-with-private-name-key-everywhere agreement. I wanted to generalize this a bit. In designing classes and other new ES abstractions there are a couple design principles that I think it is important that we follow: 1) Everything works with plain objects. Objects (and functions) are the primitive abstraction mechanisms of ES. Any new functionality we add must be applicable and available to plain vanilla singleton objects. Anti-example: super keyword that is only available in a class declaration Acceptable solution: super keyword is available for both class declaration and object literals. 2) Anything that can be done declaratively can also be done imperatively. Imperative/reflective object construction is a power feature of ES that has been widely exploited by everyday developers as well as metaprogrammers. Any new object capabilities that we make available via declarative constructs must also be available via an imperative API. Anti-example: functions definitions using super keyword may only occur within an object literal or class declaration. Acceptable solution: Object.defineMethod can be used to bind an externally defined function that uses the super keyword to a specific object. I don't expect that anybody will significantly disagree with either of these principles. But I think it is good to explicitly articulate them and make sure we have agreement one them. Sometimes we spend a lot of time discussing an idea that doesn't or can't conform to these principles. Allen ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Design principles for extending ES object abstractions
On Jul 8, 2011, at 3:49 PM, Allen Wirfs-Brock wrote: On Jul 8, 2011, at 2:58 PM, Brendan Eich wrote on the thread using Private name objects for declarative property definition. : But whatever the class syntax, and the disposition of private in class and even classes in ES.next, I agree we should expect private declarative and expression forms to work the same in object initialisers and in classes. It would be good to get everyone buying into this private-means-property-with-private-name-key-everywhere agreement. I wanted to generalize this a bit. In designing classes and other new ES abstractions there are a couple design principles that I think it is important that we follow: 1) Everything works with plain objects. Objects (and functions) are the primitive abstraction mechanisms of ES. Any new functionality we add must be applicable and available to plain vanilla singleton objects. Anti-example: super keyword that is only available in a class declaration Acceptable solution: super keyword is available for both class declaration and object literals. 2) Anything that can be done declaratively can also be done imperatively. Imperative/reflective object construction is a power feature of ES that has been widely exploited by everyday developers as well as metaprogrammers. Any new object capabilities that we make available via declarative constructs must also be available via an imperative API. Anti-example: functions definitions using super keyword may only occur within an object literal or class declaration. Acceptable solution: Object.defineMethod can be used to bind an externally defined function that uses the super keyword to a specific object. I don't expect that anybody will significantly disagree with either of these principles. But I think it is good to explicitly articulate them and make sure we have agreement one them. Sometimes we spend a lot of time discussing an idea that doesn't or can't conform to these principles. +1. Note that generators as we've prototyped them have the same imperative/reflective constructor as functions: Function. You just use yield in the body string. In working on generators for standardization, we proposed requiring * after function at the head to distinguish (for readers) generator functions from non-generator functions, since yield in the body is sometimes far removed from the start of the function. But the stronger reason for function* as mandator generator syntax introducer came when we considered yield* (yield from in Python's PEP380): the utility of a zero-iterations basis case for a sub-generator. As Dave noted, otherwise you'd have to write function* () {if (false) yield;} or some such. Does this mean we need a distinguished generator function constructor, e.g. Function.createGenerator? Probably so, for the stronger (zero-iterations, empty generator basis case) and for symmetry. Not a big deal but something to consider. In any case, I like the generalizations you make here. /be___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Design principles for extending ES object abstractions
On Jul 8, 2011, at 3:59 PM, Brendan Eich wrote: But the stronger reason for function* as mandator mandatory of course (need new keyboard). generator syntax introducer came when we considered yield* (yield from in Python's PEP380): the utility of a zero-iterations basis case for a sub-generator. As Dave noted, otherwise you'd have to write function* () {if (false) yield;} or some such. function () {if (false) yield;} of course -- the hypothesis there assumes no * after function, in which case you must write a dead yield to make the function be a generator in the first place. /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
RE: Design principles for extending ES object abstractions
I agree wholeheartedly with these. In fact, I'd go further on (2), and say Anything that can be done declaratively can also be done imperatively, using ES5 syntax. ES.next will have two syntaxes running on a single runtime, sharing objects across a shared heap. I think we should ensure that all relevant semantics of ES.next are pushed down into the shared runtime, and exposed through libraries available to both syntaxes, to ensure full interoperability between the two. The one additional place I know of in existing ES.next proposals where I believe this principle is not yet met is the ability to define a module from ES5 syntax, and consume it from ES.next syntax. Inline with these principles, I expect the module loader API should offer a way to imperatively define a module that can be later loaded using module loader APIs. Luke From: es-discuss-boun...@mozilla.org [mailto:es-discuss-boun...@mozilla.org] On Behalf Of Allen Wirfs-Brock Sent: Friday, July 08, 2011 3:49 PM To: es-discuss@mozilla.org Subject: Design principles for extending ES object abstractions [snip...] I wanted to generalize this a bit. In designing classes and other new ES abstractions there are a couple design principles that I think it is important that we follow: 1) Everything works with plain objects. Objects (and functions) are the primitive abstraction mechanisms of ES. Any new functionality we add must be applicable and available to plain vanilla singleton objects. Anti-example: super keyword that is only available in a class declaration Acceptable solution: super keyword is available for both class declaration and object literals. 2) Anything that can be done declaratively can also be done imperatively. Imperative/reflective object construction is a power feature of ES that has been widely exploited by everyday developers as well as metaprogrammers. Any new object capabilities that we make available via declarative constructs must also be available via an imperative API. Anti-example: functions definitions using super keyword may only occur within an object literal or class declaration. Acceptable solution: Object.defineMethod can be used to bind an externally defined function that uses the super keyword to a specific object. I don't expect that anybody will significantly disagree with either of these principles. But I think it is good to explicitly articulate them and make sure we have agreement one them. Sometimes we spend a lot of time discussing an idea that doesn't or can't conform to these principles. Allen ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Design principles for extending ES object abstractions
On Jul 8, 2011, at 4:05 PM, Luke Hoban wrote: I agree wholeheartedly with these. In fact, I’d go further on (2), and say “Anything that can be done declaratively can also be done imperatively, using ES5 syntax”. The problem here is that some new syntax cannot be faked with old syntax, namely function calls, without quoting code in strings. This is not usable. A second problem is that adding API functions means injecting more names into some extant object, probably not the global object. Must all new APIs be Object.createPrivateName and only that? We have already accepted proposals that use built-in modules instead, so that there is no name pollution. ES.next will have two syntaxes running on a single runtime, sharing objects across a shared heap. The shared heap imposes some requirements on us, including that old code operating using old syntax with known semantics on a new object must not behave badly (details vary). But this does *not* require that all new features, especially those requiring new syntax to be *usable*, must have old, string-based, name-pollusting API functions. /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: using Private name objects for declarative property definition.
On Jul 8, 2011, at 3:24 PM, Brendan Eich wrote: On Jul 8, 2011, at 2:43 PM, Andreas Rossberg wrote: Point = { //private members [__x]: 0, [ __y]: 0, [__validate](x,y) { return typeof x == 'number' typeof y = 'number'}, //public members new(x,y) { if (!this[__validate](x,y)) throw invalid; return this | { [__x]: x, [__y]: y } }; add(anotherPoint) { return this.new(this[__x]+another[__x], this[__y]+another[__y]) } } I like this notation most, because it can be generalised in a consistent manner beyond the special case of private names: there is no reason that the bit in brackets is just an identifier, we could allow arbitrary expressions. Then the shape of the object is not static. Perhaps this is worth the costs to implementations and other analyzers (static program analysis, human readers). We should discuss a bit more first, as I just wrote in reply to Allen. This is one of the reason I think I slightly prefer the @ approach. @ can be defined similarly to . in that it must be followed by an identifier and that the identifier must evaluate to a private name object. The latter in the general case would have to be a runtime check but in many common cases could be statically verified. Even this doesn't guarantee that we statically know the shape. Consider: function f(a,b) { return { @a: 1, @b: 2 } }; const x=Name.create(); const y=Name.create(); let obj1 = f(x,y); let obj2 = f(y,x); let obj3 = f(x,x); let obj4 = f(y,y); So the notation would be the proper dual to bracket access notation. From a symmetry and expressiveness perspective, this is very appealing. It does help avoid eval abusage, on the upside. If you mean something like: let propName = computeSomePropertyName(); let obj = eval(({+propName+: null})); I'd say whoever does that doesn't know the language well enough as their are already good alternatives such as: let obj = {}; obj[propName] = null; or let obj = new Object; Object.defineProperty(obj, propName,{value:null, writable: true, enumerable: true, configurable: true}); I know that people do stupid and unnecessary eval tricks today but I doubt that adding yet another non-eval way to accomplish the same thing is going to stop that. The basic problem they have is not knowing the language and make the language bigger isn't likely to make things any better for them. Proceeding bottom-up, with orthogonal gap-filling primitives that compose well, is our preferred way for Harmony. Private name objects without new syntax, requiring bracket-indexing, won in part by filling a gap without jumping to premature syntax with novel binding semantics (see below). Here, with obj = { [expr]: value } as the way to compute a property name in an object initialiser (I must not write object literal any longer), we are proceeding up another small and separate hill. But, is this the right design for object initialisers (which the normative grammar does call ObjectLiterals)? Another concern is that it creates another look-ahead issue for the unify blocks and object initializers proposal. Notation-wise, I think people would get used to using brackets. I see no good reason to introduce yet another projection syntax, like @. Agreed that unless we use @ well for both property names in initialisers and private property access, and in particular if we stick with bracketing for access, then square brackets win for property naming too -- but there's still the loss-of-static-shape issue. There is also an argument to me made that [ ] and @ represent two different use cases: computed property access and private property access and for that reason there should be a syntactic distinction between them. Whether additional sugar is worthwhile -- e.g. private declarations -- remains to be explored. (To be honest, I haven't quite understood yet in what sense such sugar would really be more declarative. Sure, it is convenient and perhaps more readable. But being declarative is a semantic property, and cannot be achieved by simple syntax tweaks.) Good point! The original name declaration via private x that Dave championed was definitely semantic: it created a new static lookup hierarchy, lexical but for names after . in expressions and before : in property assignments in object literals. This was, as Allen noted, controversial and enough respected folks on es-discuss (I recall Andrew Dupont in particular) and in TC39 reacted negatively that we separated and deferred it. I do not know how to revive it productively. Actually, I think you can blame be rather than Dave for the dual lookup hierarchy. Dave (and Sam's) original proposal did have special semantics for private name declarations bug only had a single lookup
RE: Design principles for extending ES object abstractions
I agree wholeheartedly with these. In fact, I'd go further on (2), and say Anything that can be done declaratively can also be done imperatively, using ES5 syntax. The problem here is that some new syntax cannot be faked with old syntax, namely function calls, without quoting code in strings. This is not usable. I think it's fine for the imperative solution to be less usable. That's the value-add of opting-in to the ES.next syntax. And of course some (most) new syntax is just syntax, and the ultimate objects it creates are ones that could have been created using some more complex path. Those don't need any library support. When Allen mentioned imperatively, I assumed he meant with a library. I'm not actually sure what other interpretation there would be. So I sort of expected that clarification to using ES5 syntax to be a no-op, though I expect it is practically quite important. A second problem is that adding API functions means injecting more names into some extant object, probably not the global object. Must all new APIs be Object.createPrivateName and only that? We have already accepted proposals that use built-in modules instead, so that there is no name pollution. I hope, and believe, there are actually not very many new runtime capabilities being added in ES.next that don't already have proposed libraries. I do think there will need to be some rationalization of the goal to use built-in modules with the reality of ES5-syntax consumers of these libraries. I'm not sure whether module loaders currently provide a way to do this that would feel accessible. ES.next will have two syntaxes running on a single runtime, sharing objects across a shared heap. The shared heap imposes some requirements on us, including that old code operating using old syntax with known semantics on a new object must not behave badly (details vary). But this does *not* require that all new features, especially those requiring new syntax to be *usable*, must have old, string-based, name-pollusting API functions. I agree, the shared heap requirement by itself does not impose this. But I believe the design principle Allen outlined should lead us to this anyway, and the value we'll offer to the many millions of existing 'text/javascript' developers through object-detectable runtime capability additions is a nice bonus :). ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Design principles for extending ES object abstractions
On Jul 8, 2011, at 4:27 PM, Luke Hoban wrote: I agree wholeheartedly with these. In fact, I’d go further on (2), and say “Anything that can be done declaratively can also be done imperatively, using ES5 syntax”. The problem here is that some new syntax cannot be faked with old syntax, namely function calls, without quoting code in strings. This is not usable. I think it’s fine for the imperative solution to be less usable. That’s the value-add of opting-in to the ES.next syntax. And of course some (most) new syntax is just syntax, and the ultimate objects it creates are ones that could have been created using some more complex path. Those don’t need any library support. When Allen mentioned “imperatively”, I assumed he meant “with a library”. I’m not actually sure what other interpretation there would be. So I sort of expected that clarification to “using ES5 syntax” to be a no-op, though I expect it is practically quite important. Mostly, although obj[foo] = blah; is also an imperative way to define a property. Also note that my intent was to restricted both principles to matters directly relating to objects even though I didn't explicitly mention objects in naming the 2nd principle. There are many things in Es.next (and ES5, for that matter) that can be done declaratively WRT constructing closures that has no API based alternative (other than eval, which I choose not to count). There is probably an argument to be made for accomplishing the somethings via reflective APIs. However, given that there is no history of that in ES I don't think we need to make it a Es.next requirement. Allen___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: using Private name objects for declarative property definition.
On Jul 8, 2011, at 4:21 PM, Allen Wirfs-Brock wrote: On Jul 8, 2011, at 3:24 PM, Brendan Eich wrote: Then the shape of the object is not static. Perhaps this is worth the costs to implementations and other analyzers (static program analysis, human readers). We should discuss a bit more first, as I just wrote in reply to Allen. This is one of the reason I think I slightly prefer the @ approach. @ can be defined similarly to . in that it must be followed by an identifier and that the identifier must evaluate to a private name object. The latter in the general case would have to be a runtime check but in many common cases could be statically verified. This does give a probabilistic edge to implementations optimizing @ for private names only (but with guards that throw on non-private-name result of evaluating what's on the right of @), and predicting or static-analyzing shape. Even this doesn't guarantee that we statically know the shape. Certainly not. And that is a change from today, which I think we ought to discuss. Because as you note below, private names could be added to objects declared by initialisers that lack any private name syntax, after the initialiser; or after a new Object or Object.create. It does help avoid eval abusage, on the upside. If you mean something like: let propName = computeSomePropertyName(); let obj = eval(({+propName+: null})); I'd say whoever does that doesn't know the language well enough as their are already good alternatives such as: let obj = {}; obj[propName] = null; or let obj = new Object; Object.defineProperty(obj, propName,{value:null, writable: true, enumerable: true, configurable: true}); No, the problem is not that there aren't other ways to do this. Or that people are too dumb. I've had real JS users, often with some Python knowledge but sometimes just proceeding from a misunderstanding of how object literals are evaluated, ask for the ability to compute property names selectively in object literals. Sure, they can add them after. That sauce for the goose is good for the private-name gander too, no? Here, with obj = { [expr]: value } as the way to compute a property name in an object initialiser (I must not write object literal any longer), we are proceeding up another small and separate hill. But, is this the right design for object initialisers (which the normative grammar does call ObjectLiterals)? Another concern is that it creates another look-ahead issue for the unify blocks and object initializers proposal. Yes, indeed. I've been thinking about that one since yesterday, without much more to show for it except this: I think we should be careful not to extend object literals in ways that create more ambiguity with blocks. I grant that the new method syntax is too sweet to give up. But I don't think {[..., {!..., etc. for # and ~ after {, are yet worth the ambiguity that is future-hostile to block vs. object literal unity. Agreed that unless we use @ well for both property names in initialisers and private property access, and in particular if we stick with bracketing for access, then square brackets win for property naming too -- but there's still the loss-of-static-shape issue. There is also an argument to me made that [ ] and @ represent two different use cases: computed property access and private property access and for that reason there should be a syntactic distinction between them. Is this different from your first point above, about implementation optimization edge? The original name declaration via private x that Dave championed was definitely semantic: it created a new static lookup hierarchy, lexical but for names after . in expressions and before : in property assignments in object literals. This was, as Allen noted, controversial and enough respected folks on es-discuss (I recall Andrew Dupont in particular) and in TC39 reacted negatively that we separated and deferred it. I do not know how to revive it productively. Actually, I think you can blame be rather than Dave for the dual lookup hierarchy. Dave (and Sam's) original proposal did have special semantics for private name declarations bug only had a single lookup hierarchy. Of course -- sorry about that, credit and blame where due ;-). /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Design principles for extending ES object abstractions
On Jul 8, 2011, at 4:27 PM, Luke Hoban wrote: I agree wholeheartedly with these. In fact, I’d go further on (2), and say “Anything that can be done declaratively can also be done imperatively, using ES5 syntax”. The problem here is that some new syntax cannot be faked with old syntax, namely function calls, without quoting code in strings. This is not usable. I think it’s fine for the imperative solution to be less usable. I think we need to agree that eval does not count, as Allen just wrote. That is, if you'd be happy if old script could call, e..g Object.evalInHarmony(...) then we're done. That’s the value-add of opting-in to the ES.next syntax. And of course some (most) new syntax is just syntax, and the ultimate objects it creates are ones that could have been created using some more complex path. Those don’t need any library support. If eval does count, we're done. If eval doesn't count, then how pray tell does old code create a generator? Not by building an interpreter in JS. When Allen mentioned “imperatively”, I assumed he meant “with a library”. I’m not actually sure what other interpretation there would be. So I sort of expected that clarification to “using ES5 syntax” to be a no-op, though I expect it is practically quite important. The issue is not with a library, it is whether the only new APIs in ES.next must be Object.uglyNameGoesHere, with string arguments for anything that can't be expressed in the old syntax (like yield in a generator). If we have built-in modules in ES.next, we shouldn't have duplicate Object.mumble APIs for them as well (Object is one of those shared-heap objects common to old and new scripts loaded against the same global with the default module loader). A second problem is that adding API functions means injecting more names into some extant object, probably not the global object. Must all new APIs be Object.createPrivateName and only that? We have already acceptedproposals that use built-in modules instead, so that there is no name pollution. I hope, and believe, there are actually not very many new runtime capabilities being added in ES.next that don’t already have proposed libraries. I do think there will need to be some rationalization of the goal to use built-in modules with the reality of ES5-syntax consumers of these libraries. I’m not sure whether module loaders currently provide a way to do this that would feel accessible. That's a good point. If we expose just one property, say Object.ModuleLoader then by my reading of the module loaders proposal, ES5 code can do whatever it wants with built-in and other new modules. Would this be enough for what you're after? The shared heap imposes some requirements on us, including that old code operating using old syntax with known semantics on a new object must not behave badly (details vary). But this does *not* require that all new features, especially those requiring new syntax to be *usable*, must have old, string-based, name-pollusting API functions. I agree, the shared heap requirement by itself does not impose this. But I believe the design principle Allen outlined should lead us to this anyway, and the value we’ll offer to the many millions of existing ‘text/javascript’ developers through object-detectable runtime capability additions is a nice bonus J. Arguing about Principles as if they were ironclad law is a good timekiller, over which TC39 will fall out of Harmony quickly if we do make the mistake of killing too much time. We need use-cases as well as abstractions like capital-P Principles. We need Aristotle as well as Plato (http://answers.yahoo.com/question/index?qid=20090513192850AAGmPAH). And we need some good taste and judgment in knowing when to ease off on the Principle-mongering. /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Design principles for extending ES object abstractions
On Jul 8, 2011, at 3:49 PM, Allen Wirfs-Brock wrote: 2) Anything that can be done declaratively can also be done imperatively. What's the imperative API for | (which has the syntactic property that it operators on newborns on the right, and cannot mutate the [[Prototype]] of an object that was already created and perhaps used with its original [[Prototype]] chain)? /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Design principles for extending ES object abstractions
I think I still haven't fully grokked what | means on array literals, but could it also be used to subclass Array? For example: function SubArray() { return SubArray.prototype | []; } SubArray.prototype = new Array; I'm not sure what Array.prototype methods would or wouldn't work on instances of SubArray. Dave On Jul 8, 2011, at 5:48 PM, Allen Wirfs-Brock wrote: On Jul 8, 2011, at 5:16 PM, Brendan Eich wrote: On Jul 8, 2011, at 3:49 PM, Allen Wirfs-Brock wrote: 2) Anything that can be done declaratively can also be done imperatively. What's the imperative API for | (which has the syntactic property that it operators on newborns on the right, and cannot mutate the [[Prototype]] of an object that was already created and perhaps used with its original [[Prototype]] chain)? Fair point and one I was already thinking about :-) For regular objects, it is Object.create. For special built-in object with literal forms, I've previously argument that | can be used to implement an imperative API: Array.create = function (proto,members) { let obj = proto | {}; Object.defineProperties(obj,members); return obj; } Basically, | is sorta half imperative operator, half declaration component. This may be good enough. It would be nice it it was and we didn't have to have additional procedural APIs for constructing instances of the built-ins. Somebody has already pointed out | won't work for built-in Date objects because they lack a literal form. I think the best solution for that is to actually reify access to a Date object's timevalue by making it a private named property. BTW, way did ES1(?) worry about allowing for alternative internal timevalue representations? If in really there really any perf issues that involve whether or not the timevalue is represented as a double or something else? Allen ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Design principles for extending ES object abstractions
On Jul 8, 2011, at 5:48 PM, Allen Wirfs-Brock wrote: On Jul 8, 2011, at 5:16 PM, Brendan Eich wrote: What's the imperative API for | (which has the syntactic property that it operators on newborns on the right, and cannot mutate the [[Prototype]] of an object that was already created and perhaps used with its original [[Prototype]] chain)? Fair point and one I was already thinking about :-) For regular objects, it is Object.create. For special built-in object with literal forms, I've previously argument that | can be used to implement an imperative API: Array.create = function (proto,members) { let obj = proto | {}; Object.defineProperties(obj,members); return obj; } And likewise for Function.create and RegExp.create. Boolean, Number, String, and Date get nothing :-P. We have a somewhat-troubled proposal in Harmony to make Function.create an alternative Function constructor that takes a leading name parameter, and then parameters and body string parameters. But perhaps that could be renamed Function.createNamed. Basically, | is sorta half imperative operator, half declaration component. This may be good enough. It would be nice it it was and we didn't have to have additional procedural APIs for constructing instances of the built-ins. Somebody has already pointed out | won't work for built-in Date objects because they lack a literal form. I think the best solution for that is to actually reify access to a Date object's timevalue by making it a private named property. That is an old idea I've brought up from time to time. If that private name were exported, you could even make useful Date subclasses (rather than Date instances that have extended proto chains). BTW, way did ES1(?) worry about allowing for alternative internal timevalue representations? If in really there really any perf issues that involve whether or not the timevalue is represented as a double or something else? The extrapolated Gregorian calendar's range in milliseconds was chosen carefully to fit in an IEEE 754 double without loss of precision. Real implementations decode the double into commonly-accessed fields that would have to track any updates to the milliseconds since (negative for before) the epoch. /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: using Private name objects for declarative property definition.
On Jul 8, 2011, at 2:58 PM, Brendan Eich wrote: On Jul 8, 2011, at 12:16 PM, Allen Wirfs-Brock wrote: The current Harmony classes proposal http://wiki.ecmascript.org/doku.php?id=harmony:classes includes the concept of private instance members and syntax for defining them. While it presents a syntax for accessing them (eg, private(foo).bar accesses the private 'bar' member of the object that is the value of foo) there does not yet appear to be consensus acceptance of this access syntax. Oh, quite the opposite -- everyone on TC39 with whom I've spoken agrees that private(this) syntax is straw that must be burned up. We need new syntax, or else we need to do at least what Dave proposed in minimal classes: defer private syntax, let programmers use private name objects and explicit [] indexing. I do think we can say a few more things about private in class that may not be in the requirements on the wiki: On the level of rationale for why class- and not instance-private, Juan Ignacio Dopazo in private correspondence made a good observation, shown by his example: class MyClass { private foo() {} bar() { var self = this; setTimeout(function () { self.foo(); }, 0); } } Because |this| is not lexical, instance- rather than class-private access that requires this. to the left of the private variable reference does not work unless you use the closure pattern explicitly and abjure |this|. The straw private(foo) syntax doesn't help if the goal is instance privacy, since the inner function has no idea (runtime, never mind compile-time) how to enforce to which particular instance self must refer. Requiring .bind(this) after the function expression passed to setTimeout can be used to work around such a hypothetical, mandatory this.-prefix instance-private restriction, but that's onerous and it can be too costly. An block lambdas would presumably also provide a solution if they were added to the language. Another observation about any class-private scheme we might consider in the current context: private instance variables in many ways (notably not for Object.freeze) act like properties, and the syntax mooted so far casts them in that light. Even if the ES5 reflective APIs, such as Object.getOwnPropertyNames, rightly skip privates on a class instance, proxies may raise the question: how does a private variable name reflect as a property name? class Point { constructor(x, y) { private x = x, y = y; } equals(other) { return private(this).x is private(other).x private(this).y is private(other).y; } ... } We cannot know how to ask for private-x from other without special syntax of some kind, either at the access point or as a binding declaration affecting all .x (and x: in object literals). So here I use the proposal's straw private(foo) syntax. Could other be a proxy that somehow has a private data record? Could other denote a class instance whose [[Prototype]] is a proxy? I claim we do not want private(foo) by itself, no .x after, to reify as an object, but if it did, then it seems to me it could be used with a proxy to reflect on private variable names. There are certainly several choices here, but the one I currently favor as simplest, which creates no new, ad-hoc concepts in the language, is that class-private instance variables are properties named by private name objects. There is more general issue about reflection on private members (and I intentionally said members here rather than names). Some of us believe that private members should never be exposed via reflection. Others of us think there are plenty of reflection use cases where it is either useful or necessary to reflect using private names. The current private name objects allows whether or not reflection is permitted using that name to be set when the private name is created. The pure declarative form in the proposed class declarations don't have any way to specify that regardless of whether or not private names are used as the underlyng implementation for private members). If it did allow such control (and private names were used in the implementation), there would still need to be a way to access the private name associated by the class declaration with a specific private member in order to reflect upon it. Sure you could use getOwnPropertyNames to access all of the property names but if that was all you had how could you be sure of the correspondence between private names and declared private members. Per the requirements for private in the classes proposal, this means Object.freeze does not freeze private-named properties, or at least does not make private-named data properties non-writable. Perhaps Object.preventExtensions should not restrict private-named properties from being added to the object. This seems strange and wrong at
Re: Design principles for extending ES object abstractions
On Jul 8, 2011, at 5:53 PM, David Herman wrote: I think I still haven't fully grokked what | means on array literals, but could it also be used to subclass Array? For example: function SubArray() { return SubArray.prototype | []; } SubArray.prototype = new Array; I'm not sure what Array.prototype methods would or wouldn't work on instances of SubArray. Instances of SubArray must mean (new SubArray) results, but those are indeed true Array instances. They simply have a prototype chain that has been extended: a = SubArray(); // or new SubArray Object.getPrototypeOf(a) - SubArray.prototype (which is an Array) Object.getPrototypeOf(SubArray.prototype) - Array.prototype and Array.prototype of course delegates to Object.prototype This allows new methods to be added to SubArray.prototype. Sometimes people try to make a new Array instance be the prototype of some other object: js var o = Object.create([]) js o[2] = 0 0 js o.length 0 js o[1] = 1 1 js o[0] = 2 2 js o.toString() js o.slice(0,1) [] js o.length = 3 3 js o.slice(0,1) [2] js o.sort(function(a,b){return a-b}).toString() 0,1,2 js o.reverse().toString() 2,1,0 Note the lack of automagic length maintenance in this case. I think this is all per ES5. /be___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Design principles for extending ES object abstractions
On Jul 8, 2011, at 5:53 PM, David Herman wrote: I think I still haven't fully grokked what | means on array literals, but could it also be used to subclass Array? For example: yes, it creates a new object that is an array instance ([[Class]]=='Array', support the length constraints, etc.) that has LHS of | as its [[Prototype]] value. (BTW, in my initial working draft for the ES6 spec. I have already purged [[Class]] from the specification) function SubArray() { return SubArray.prototype | []; } SubArray.prototype = new Array; I'm not sure what Array.prototype methods would or wouldn't work on instances of SubArray. All of them. They are all generic. However, subarray instances have all the internal state and methods that make them true arrays so even if some of the inherited Array methods weren't generic they would still work. Allen ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Design principles for extending ES object abstractions
On Jul 8, 2011, at 6:03 PM, Brendan Eich wrote: On Jul 8, 2011, at 5:48 PM, Allen Wirfs-Brock wrote: On Jul 8, 2011, at 5:16 PM, Brendan Eich wrote: What's the imperative API for | (which has the syntactic property that it operators on newborns on the right, and cannot mutate the [[Prototype]] of an object that was already created and perhaps used with its original [[Prototype]] chain)? Fair point and one I was already thinking about :-) For regular objects, it is Object.create. For special built-in object with literal forms, I've previously argument that | can be used to implement an imperative API: Array.create = function (proto,members) { let obj = proto | {}; Object.defineProperties(obj,members); return obj; } And likewise for Function.create and RegExp.create. Boolean, Number, String, and Date get nothing :-P. Actually in the | proposal I define it to work with boolean, number, and string literals on the LHS. Sorta useless but I included them so the complete set of literals was covered. So it really is only Date that didn't get invited to the party. We have a somewhat-troubled proposal in Harmony to make Function.create an alternative Function constructor that takes a leading name parameter, and then parameters and body string parameters. But perhaps that could be renamed Function.createNamed. I think that create methods on Constructors should generally follow the argument pattern of Object.create. Things that don't should get a different name. Basically, | is sorta half imperative operator, half declaration component. This may be good enough. It would be nice it it was and we didn't have to have additional procedural APIs for constructing instances of the built-ins. Somebody has already pointed out | won't work for built-in Date objects because they lack a literal form. I think the best solution for that is to actually reify access to a Date object's timevalue by making it a private named property. That is an old idea I've brought up from time to time. If that private name were exported, you could even make useful Date subclasses (rather than Date instances that have extended proto chains). BTW, way did ES1(?) worry about allowing for alternative internal timevalue representations? If in really there really any perf issues that involve whether or not the timevalue is represented as a double or something else? The extrapolated Gregorian calendar's range in milliseconds was chosen carefully to fit in an IEEE 754 double without loss of precision. Real implementations decode the double into commonly-accessed fields that would have to track any updates to the milliseconds since (negative for before) the epoch. Seems like this could be an invisible implementation detail. An it is really worth the effort. How often does anybody set Date components in a situation that is so time critical that this would matter. (any shouldn't dates be immutable...oh well) Allen ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Design principles for extending ES object abstractions
On Jul 8, 2011, at 6:38 PM, Allen Wirfs-Brock wrote: And likewise for Function.create and RegExp.create. Boolean, Number, String, and Date get nothing :-P. Actually in the | proposal I define it to work with boolean, number, and string literals on the LHS. Sorta useless but I included them so the complete set of literals was covered. So it really is only Date that didn't get invited to the party. For ES4 we entertained date literals based ISO 8601 T literals. Couldn't justify 'em, the use-cases were all unlikely hardcodings. We have a somewhat-troubled proposal in Harmony to make Function.create an alternative Function constructor that takes a leading name parameter, and then parameters and body string parameters. But perhaps that could be renamed Function.createNamed. I think that create methods on Constructors should generally follow the argument pattern of Object.create. Things that don't should get a different name. Agreed. The extrapolated Gregorian calendar's range in milliseconds was chosen carefully to fit in an IEEE 754 double without loss of precision. Real implementations decode the double into commonly-accessed fields that would have to track any updates to the milliseconds since (negative for before) the epoch. Seems like this could be an invisible implementation detail. Certainly, it is invisible. An it is really worth the effort. How often does anybody set Date components in a situation that is so time critical that this would matter. (any shouldn't dates be immutable...oh well) SunSpider, cough. /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss