Re: Making super work outside a literal?
On Jun 22, 2011, at 4:02 PM, Axel Rauschmayer wrote: From: Brendan Eich bren...@mozilla.com Date: June 23, 2011 0:21:17 GMT+02:00 Is there no per-call cost whatsoever to adding static super? No, it's static -- an internal property of the function object set once on creation and (in my view) never changed thereafter unless unobservably (Allen's point about optimizing Object.defineMethod when handing off an otherwise-useless function expression). How does a function get access to its properties? Doesn’t the ability to access thisFunction incur any costs? I would expect static super to work as follows: var Sub = Super | { foo: function me(x) { me.super.foo.call(this, x); } } Does the named function expression cost nothing here? This is entirely beside the point. Dynamic |super| as Sean proposes requires *every call site* to pass the |here| parameter or something derived from it, no way around that. Paying for 'super' if you use it, buying by the yard, is not a problem. Making *every function call* in the language slow, increasing register pressure, etc. -- absent aggressive inference to identify callees and specialize call sites to them (inlining VMs do this but it can backfire, so there will be a default case that can't do this) -- is a big problem. /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Analog to Object.getPropertyDescriptor() for *changing* a property value?
On Jun 22, 2011, at 3:55 PM, Axel Rauschmayer wrote: You should not in general mutate shared prototype state via one of N delegating objects. It leads to trouble. What’s the difference to a global variable? The list is long. How about this? You have a bunch of local variables in various functions referencing different objects. But by some obscure yet legal means you use one such reference to mutate the prototype object they share in common, so all the others appear to be mutated too. This is not inherently evil, but it's usually a bug. Usually the other different objects do not wish to be mutated so indirectly, and the users of their properties (own and delegated) in those various functions may not be prepared for such a change. Consider monkey-patching. You want to add value to a shared prototype, but you might break for-in loops. Ok, that can be solved with ES5 Object.defineProperty -- but not if the mutation breaks some other invariant. It's occasionally useful to do AOP on shared prototypes. My point is that you ought to address them directly by their true names (Array.prototype, e.g.; not Object.getPrototypeOf(someArray)). /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Making super work outside a literal?
On Jun 23, 2011, at 7:04 AM, Brendan Eich wrote: On Jun 22, 2011, at 4:02 PM, Axel Rauschmayer wrote: Does the named function expression cost nothing here? This is entirely beside the point. Dynamic |super| as Sean proposes requires *every call site* to pass the |here| parameter or something derived from it, no way around that. Paying for 'super' if you use it, buying by the yard, is not a problem. Making *every function call* in the language slow, increasing register pressure, etc. -- absent aggressive inference to identify callees and specialize call sites to them (inlining VMs do this but it can backfire, so there will be a default case that can't do this) -- is a big problem. /be If you don't have actual implementation experience with dynamic languages I think you guys will just have to take Brendan's and my word for this. Dynamic super would have unacceptable overhead on every function call. Dynamic super may be conceptually slightly cleaner conceptually in that it eliminates the need for Object.defineMethod. However, the cost is unacceptably high. Pragmatically, the choice is between static super and no super. Insisting on dynamic super just pushes the discussion in the no super direction. Allen___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Analog to Object.getPropertyDescriptor() for *changing* a property value?
On Jun 21, 2011, at 9:50 PM, Axel Rauschmayer wrote: That sounds like the opposite argument you are making with regard to the hypothetical |here|: BTW I do not agree we can or should try to reserve 'here' or expose the method's home object -- that breaks abstractions built using prototypes. 'super' does not, because it always goes to the [[Prototype]] of the object in which the method was defined. The prototype seems to be the best location for shared data (such as the number of instances of a class). Wouldn’t you also want to abstract from the exact location of such a property? Behaviorally, you shouldn't care how an object is implemented. Whether a particular property is implemented as an own property or is inherited from a prototype is an implementation detail that should be irrelevant to client code that is using an object. Such implementation details should be, at least conceptually, encapsulated by the object. If you are dynamically inspecting or modifying details of the implementation of an object you have moved into the realm of reflection. Object.getPrototypeOf, Object.getOwnPropertyDescriptor, etc. are reflection operations. Generally it is poor practice to design an object such that client code needs to enter the domain of reflection to interact with the object. If client code uses these operations it is breaking implementation encapsulation. One of the reason that the reflection functions added by ES5 are defined on Object rather than Object.prototype is to avoid polluting the public interface of every object with additional reflective operations over and beyond the legacy ones already defined in ES1-3. Arguably, even that separation isn't enough as Object really is in the application layer of the system rather than the reflection layer. In the long run it would be good if we could further separate the reflection layer by migrating to a Mirrors-based reflection model. See my series of blogs post about Mirrors for JavaScript http://www.wirfs-brock.com/allen/posts/228 for more details. We probably still need to add some reflection methods to Object. defineMethod is one strong candidate. However, we really should be quite careful about what goes there as it is just too easy to use such Object.* functions to violate an object's implementation encapsulation. Allen ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Analog to Object.getPropertyDescriptor() for *changing* a property value?
Hi, In a the provided example, you assign a value. Can't you just use a getter/setter pair on the prototype chain? It sounds like it would solve your use case (without the syntax sugar). On ES5 - 8.12.5 step 4 5, the prototype chain is climbed to look up for an accessor and if there is a setter (anywhere on the prototype chain), this setter is called. Combined with the fact that a getter naturally do the same thing for getters, you have your way to change a value directly on the prototype chain. Of course, if you want to change the property descriptor on the prototype chain, none of what I said apply. David Le 21/06/2011 17:24, Axel Rauschmayer a écrit : As a loose analog to the prototype-chain-traversing getPropertyDescriptor(), I would still like to have something that allows one to easily *change* properties higher up the prototype chain (e.g. to use a prototype to share state). Maybe it would be enough to just have Object.getDefiningObject(obj, propName): http://www.mail-archive.com/es-discuss@mozilla.org/msg06652.html But I can also imagine syntactic sugar: obj.foo := abc desugars to Object.getDefiningObject(obj, foo).foo = abc ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Analog to Object.getPropertyDescriptor() for *changing* a property value?
I guess my argument goes like this: - If the prototype of an instance loosely corresponds to a class, why shouldn’t we have class variables/class state? - If the location for reading data is abstracted over, why isn’t the location for writing data? On Jun 23, 2011, at 12:29 , Allen Wirfs-Brock wrote: On Jun 21, 2011, at 9:50 PM, Axel Rauschmayer wrote: That sounds like the opposite argument you are making with regard to the hypothetical |here|: BTW I do not agree we can or should try to reserve 'here' or expose the method's home object -- that breaks abstractions built using prototypes. 'super' does not, because it always goes to the [[Prototype]] of the object in which the method was defined. The prototype seems to be the best location for shared data (such as the number of instances of a class). Wouldn’t you also want to abstract from the exact location of such a property? Behaviorally, you shouldn't care how an object is implemented. Whether a particular property is implemented as an own property or is inherited from a prototype is an implementation detail that should be irrelevant to client code that is using an object. Such implementation details should be, at least conceptually, encapsulated by the object. If you are dynamically inspecting or modifying details of the implementation of an object you have moved into the realm of reflection. Object.getPrototypeOf, Object.getOwnPropertyDescriptor, etc. are reflection operations. Generally it is poor practice to design an object such that client code needs to enter the domain of reflection to interact with the object. If client code uses these operations it is breaking implementation encapsulation. One of the reason that the reflection functions added by ES5 are defined on Object rather than Object.prototype is to avoid polluting the public interface of every object with additional reflective operations over and beyond the legacy ones already defined in ES1-3. Arguably, even that separation isn't enough as Object really is in the application layer of the system rather than the reflection layer. In the long run it would be good if we could further separate the reflection layer by migrating to a Mirrors-based reflection model. See my series of blogs post about Mirrors for JavaScript http://www.wirfs-brock.com/allen/posts/228 for more details. We probably still need to add some reflection methods to Object. defineMethod is one strong candidate. However, we really should be quite careful about what goes there as it is just too easy to use such Object.* functions to violate an object's implementation encapsulation. Allen -- Dr. Axel Rauschmayer a...@rauschma.de twitter.com/rauschma home: rauschma.de blog: 2ality.com ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Analog to Object.getPropertyDescriptor() for *changing* a property value?
How about this? You have a bunch of local variables in various functions referencing different objects. But by some obscure yet legal means you use one such reference to mutate the prototype object they share in common, so all the others appear to be mutated too. This is not inherently evil, but it's usually a bug. Usually the other different objects do not wish to be mutated so indirectly, and the users of their properties (own and delegated) in those various functions may not be prepared for such a change. So you are mainly worried about mutating methods? My main concern is encapsulation: If you have class methods, I would want to give them the option of having class state. I want the same rules that hold for the object level (state + behavior) to hold for the meta-object level. Consider monkey-patching. You want to add value to a shared prototype, but you might break for-in loops. Ok, that can be solved with ES5 Object.defineProperty -- but not if the mutation breaks some other invariant. I would probably never use for-in loops in ES.next. It's occasionally useful to do AOP on shared prototypes. My point is that you ought to address them directly by their true names (Array.prototype, e.g.; not Object.getPrototypeOf(someArray)). Right, the latter looks really bad. I would rely that the same kind of unique name that took me to a class method (via this) will also take me to a class variable (also via this). If I invoke the method via this, e.g. Array.prototype.myClassMethod(), then this.myClassVariable would *still* work. -- Dr. Axel Rauschmayer a...@rauschma.de twitter.com/rauschma home: rauschma.de blog: 2ality.com ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Analog to Object.getPropertyDescriptor() for *changing* a property value?
There are indeed a few ways around this: Naming the prototype, using an array with a single element, etc. On Jun 23, 2011, at 13:19 , David Bruant wrote: Hi, In a the provided example, you assign a value. Can't you just use a getter/setter pair on the prototype chain? It sounds like it would solve your use case (without the syntax sugar). On ES5 - 8.12.5 step 4 5, the prototype chain is climbed to look up for an accessor and if there is a setter (anywhere on the prototype chain), this setter is called. Combined with the fact that a getter naturally do the same thing for getters, you have your way to change a value directly on the prototype chain. Of course, if you want to change the property descriptor on the prototype chain, none of what I said apply. David Le 21/06/2011 17:24, Axel Rauschmayer a écrit : As a loose analog to the prototype-chain-traversing getPropertyDescriptor(), I would still like to have something that allows one to easily *change* properties higher up the prototype chain (e.g. to use a prototype to share state). Maybe it would be enough to just have Object.getDefiningObject(obj, propName): http://www.mail-archive.com/es-discuss@mozilla.org/msg06652.html But I can also imagine syntactic sugar: obj.foo := abc desugars to Object.getDefiningObject(obj, foo).foo = abc -- Dr. Axel Rauschmayer a...@rauschma.de twitter.com/rauschma home: rauschma.de blog: 2ality.com ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Analog to Object.getPropertyDescriptor() for *changing* a property value?
Here is a code snippet: var p = {}; (function(){ var a; Object.defineProperty(p, 'a', {get:function(){return a;}, set:function(v){a = v;}}); })(); var o1 = Object.create(p); var o2 = Object.create(p); o1.a = 1; console.log(o1.a, o2.a); // 1 1 o2.a = 37; console.log(o1.a, o2.a); // 37 37 'a' is an own property neither of o1 nor o2. (Firefox may say the opposite, but I've been told, it's fixed and going to be released on FF 6 or 7 [1] [2]). a value (so to say) is shared among the instances. David [1] https://bugzilla.mozilla.org/show_bug.cgi?id=636989 [2] https://bugzilla.mozilla.org/show_bug.cgi?id=637994 Le 23/06/2011 13:19, David Bruant a écrit : Hi, In a the provided example, you assign a value. Can't you just use a getter/setter pair on the prototype chain? It sounds like it would solve your use case (without the syntax sugar). On ES5 - 8.12.5 step 4 5, the prototype chain is climbed to look up for an accessor and if there is a setter (anywhere on the prototype chain), this setter is called. Combined with the fact that a getter naturally do the same thing for getters, you have your way to change a value directly on the prototype chain. Of course, if you want to change the property descriptor on the prototype chain, none of what I said apply. David Le 21/06/2011 17:24, Axel Rauschmayer a écrit : As a loose analog to the prototype-chain-traversing getPropertyDescriptor(), I would still like to have something that allows one to easily *change* properties higher up the prototype chain (e.g. to use a prototype to share state). Maybe it would be enough to just have Object.getDefiningObject(obj, propName): http://www.mail-archive.com/es-discuss@mozilla.org/msg06652.html But I can also imagine syntactic sugar: obj.foo := abc desugars to Object.getDefiningObject(obj, foo).foo = abc ___ 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: imperative vs functional APIs or: how to work with constant objects?
Javascript data structure operations: some of the APIs are imperative, some are functional, some are mixed. Having functional equivalents to all imperative data structure operations is necessary to support expression-oriented programming over constant data structures. imperative here refers to operations that are designed to be used as statements, changing inputs by side-effect, while functional refers to operations designed to be used as expressions, leaving their inputs unchanged. .. Hi Claus, interesting idea. Given The idea of {x: a, y: b, ...: c} – record extension and row capturehttp://successor-ml.org/index.php?title=Functional_record_extension_and_row_capture – for structuring and destructuring comes to mind, but ES objects are a different animal compared to ML records. For example, which properties of c are copied into the object being initialized by {x: a, y: b, ...: c}? enumerable? own? etc. (from http://wiki.ecmascript.org/doku.php?id=harmony:destructuring#issues) you could say const older_obj = { age: 2, ...: obj }; But as the note says, there are a bunch of open questions that need to be settled before this becomes a serious candidate. We should examine how this interacts with other aspects of extended object literals. Hi Mark, thanks for the pointer. Yes, there used to be a large body of literature just on the various ways of factoring a record operations API, mostly driven by their use for semantics of object-oriented language variations and by their type system difficulties ('record calculi', 'semantics of inheritance/oo', 'typing record concatenation', ..). Record/object extension is one of the recurring ideas, and since it is based on record/object literals + a base record/object, most of the design choices follow from the extended object literals. My immediate concern is simply: given the choices already made for Javascript object operations, why don't we have a complete set of operations for both in-place and copying updates? The current situation at the object level is as if JS only had '+=' and '-=', but not '+' and '-', at the level of numbers. As for the set of operations, JS has already chosen that updating a non-existing property extends the object, so update and extension are mixed in one operation. There is a separate operation for removing properties and there are tests for the existence of properties, both locally and in the prototype chain. If there were functional equivalents to the imperative base operations obj.prop = value; delete obj.prop; then one could build more complex operations from there, instead of from scratch. delete already returns the object, but assignment returns the value, and overloading delete to produce an object copy if the input object is frozen would be confusing, so we need new syntax, and object extension with extended object literals could do. Assuming that the extended object is copied as is, modified as specifiable by the extended object literal syntax, I'd assume { prop : value; ...: obj } to be a (shallow) copy of obj, identical (including prototype and meta properties such as frozen/configurable/..) in all but prop. If I don't want to copy prototype or meta-properties, I'd need to use records instead of objects, or get some help from the extended object literal syntax. But one shouldn't try to fit everything into the object literal syntax, either - once it is accepted that copying objects is useful, there could be an Object.filter operation, eg, to filter out properties based on their meta-properties (enumerable, own, ..): Object.filter(predicate,obj) would return a (shallow) copy of obj with properties filtered according to some predicate function. To avoid an additional operator for functional delete, one might add a delete directive to extended object literal syntax, and have { delete prop; ...: obj} be a (shallow) copy of obj, identical in all but prop. Wouldn't that be sufficient as a baseline? If not, what is missing? Is extended object literal syntax capable of handling this slightly extended range of use? Btw, should there be a way to un-freeze object copies (typical application is to make a non-frozen copy of a frozen array, update it incrementally and in-place in a loop, then freeze the resulting array copy for further use)? In sum, copying object modifications are useful for programmers, and taking them into consideration might ease the constraints for ES language design (not everything has to fit into the simple in-place update all-permitted-or-all forbidden scheme - copying updates offer a third option). Claus The standard APIs for these fundamental operations are imperative and so are not available on frozen objects (this also applies to the record strawman, for instance). How does one achieve the effects of these operations on frozen objects, by creating modified copies? For an expression-oriented style of programming where no object is ever modified in place, it
Re: Making super work outside a literal?
On Jun 23, 2011, at 12:31 PM, Axel Rauschmayer wrote: This is entirely beside the point. Dynamic |super| as Sean proposes requires *every call site* to pass the |here| parameter or something derived from it, no way around that. Paying for 'super' if you use it, buying by the yard, is not a problem. Making *every function call* in the language slow, increasing register pressure, etc. -- absent aggressive inference to identify callees and specialize call sites to them (inlining VMs do this but it can backfire, so there will be a default case that can't do this) -- is a big problem. I believe you about the dynamic super. I can summarize my question as follows: - Making super or current object available to a function incurs costs. If it is statically bound (via a [[Super]] or [[Current]] internal property then there is only a cost (and a small one) on actual accesses of super. EG, Object.defineMethod(obj,'foo, function() {return super.foo() /*cost to access [[Super]] + cost to access this */}); If dynamically bound (on each property lookup of the method) then there is a cost on every function call to pass the current object reference to the function invocation. This cost is in addition to the const of passing thethis reference. - Making current function available to a function does not incur costs? This is *not* an extra parameter, then? the current function is made available via the function name in a function declaration: var q= -1; Object.defineMethod(objfoo,function bar(a,b,c) {return q*bar.length /* returns -3 */}); bar is a lexically scoped binding just like q. No per call perimeters are involved. The cost to access bar is approximately the same as the cost of accessing q (exact costs subject to details of how lexically scoped access is implemented) Allen ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Making super work outside a literal?
On Thu, Jun 23, 2011 at 4:56 AM, Allen Wirfs-Brock al...@wirfs-brock.com wrote: If you don't have actual implementation experience with dynamic languages I think you guys will just have to take Brendan's and my word for this. I now understand and fully agree with your performance arguments against dynamic super. However, I still believe that the dynamic super semantics are largely superior to the static super semantics. Fortunately, I think the dynamic super semantics can be simulated via static super, thus yielding the best of both approaches! Here's what I propose: Terminology: super function - a function that uses |super| super delegate function - A function which delegates to a super function stored in its [[SuperFunction]] internal property, passing a |super| binding stored in its [[Super]] internal property. Semantics: When a super function is assigned as a method or accessor getter or setter either via an object literal, Object.defineProperty or direct assignment, a super delegate function is created and assigned instead whose [[SuperFunction]] internal property is the super function being assigned, and whose [[Super]] internal property is the [[Prototype]] internal property of the object being assigned to. When a super delegate function is subsequently accessed from an object, e.g. o.methodThatUsesSuper or Object.getOwnPropertyDescriptor(o, accessorWithGetterOrSetterThatUsesSuper), this resolves to the [[TargetFunction]] internal property of the super delegate function. When a super function is called directly, e.g. superFunction() or superFunction.call(this), |super| is resolved as the [[Prototype]] internal property of the |this| of the call. When a super delegate function is called, e.g. o.methodThatUsesSuper(), o.accessorWithGetterThatUsesSuper, or o.accessorWithSetterThatUsesSuper = foo, |super| is bound to the super delegate function's [[Super]] internal property. The equivalence class of a super function in == and === consists of the super function and all of its corresponding super delegate functions, to illustrate: let f = function(){return super.x}, o1 = {method: f}, o2 = Object.create(null, {method: {get: f}}); assert(f === o1.method); // true assert(o1.method === Object.getOwnPropertyDescriptor(o2, method).get); // true Thus, the static super implementation is completely abstracted from the user, and they benefit from the full dynamic super semantics! I'm sure there are still details to work out, but it seems quite promising to me. Cheers, Sean Eagan ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Making super work outside a literal?
On Jun 23, 2011, at 2:56 PM, Axel Rauschmayer wrote: - Making current function available to a function does not incur costs? This is *not* an extra parameter, then? the current function is made available via the function name in a function declaration: var q= -1; Object.defineMethod(objfoo,function bar(a,b,c) {return q*bar.length /* returns -3 */}); bar is a lexically scoped binding just like q. No per call perimeters are involved. The cost to access bar is approximately the same as the cost of accessing q (exact costs subject to details of how lexically scoped access is implemented) Isn’t bar a named function expression and won't this binding be local to the expression (unlike q)? But I don’t think that changes the cost. Yes, that was the reason for my final comment about the exact cost. Depending upon the implementation details there may be minor difference in the cost of accessing bar and q. However, any such difference is likely to be small and of little significance. Technically, according to the language specification, q, bar, and the function arguments a,b,c are in different scope contours. Allen ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Making super work outside a literal?
A couple corrections below: On Thu, Jun 23, 2011 at 9:14 AM, Sean Eagan seaneag...@gmail.com wrote: When a super delegate function is subsequently accessed from an object, e.g. o.methodThatUsesSuper or Object.getOwnPropertyDescriptor(o, accessorWithGetterOrSetterThatUsesSuper), this resolves to the [[TargetFunction]] internal property of the super delegate function. [[TargetFunction]] should be [[SuperFunction]] The equivalence class of a super function in == and === consists of the super function and all of its corresponding super delegate functions Actually, it's simpler than this. One can never obtain a reference to a super delegate function, only call it as a method or accessor getter or setter. Cheers, Sean Eagan ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Analog to Object.getPropertyDescriptor() for *changing* a property value?
On Jun 23, 2011, at 4:54 AM, Axel Rauschmayer wrote: How about this? You have a bunch of local variables in various functions referencing different objects. But by some obscure yet legal means you use one such reference to mutate the prototype object they share in common, so all the others appear to be mutated too. This is not inherently evil, but it's usually a bug. Usually the other different objects do not wish to be mutated so indirectly, and the users of their properties (own and delegated) in those various functions may not be prepared for such a change. So you are mainly worried about mutating methods? No, any data property. My main concern is encapsulation: If you have class methods, I would want to give them the option of having class state. I want the same rules that hold for the object level (state + behavior) to hold for the meta-object level. The prototype is not the class. Consider monkey-patching. You want to add value to a shared prototype, but you might break for-in loops. Ok, that can be solved with ES5 Object.defineProperty -- but not if the mutation breaks some other invariant. I would probably never use for-in loops in ES.next. That does not address the general problem. Please read the whole sentence including the part I wrote after the --. /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Making super work outside a literal?
Ok, sorry it took me so long, but I agree that the general idea of Object.defineMethod is the best solution. I think it can be improved upon though, by adding the following: Ability to use any property descriptor attributes when defining methods. Ability to assign accessor getters and setters as well which use the assigned to object as |super|. Avoid having to add a defineMethod proxy trap. Here are the two potential solutions I have in mind for this: * Have Object.defineProperty create new functions with updated [[Super]] when the value, get, or set attributes are functions that use super. * Add an Object.defineSuperProperty which is the same as the existing Object.defineProperty except that it creates new functions with updated [[Super]] when the value, get, or set attributes are functions that use super. It could potentially throw if no super functions are passed. This one may still require a defineSuperProperty proxy trap, not sure. Also, I wonder if there is a need for a Function.prototype.isSuper similar to Function.prototype.isGenerator? On Thu, Jun 23, 2011 at 10:51 AM, Allen Wirfs-Brock al...@wirfs-brock.com wrote: On Jun 23, 2011, at 3:14 PM, Sean Eagan wrote: On Thu, Jun 23, 2011 at 4:56 AM, Allen Wirfs-Brock al...@wirfs-brock.com wrote: If you don't have actual implementation experience with dynamic languages I think you guys will just have to take Brendan's and my word for this. I now understand and fully agree with your performance arguments against dynamic super. However, I still believe that the dynamic super semantics are largely superior to the static super semantics. Fortunately, I think the dynamic super semantics can be simulated via static super, thus yielding the best of both approaches! Here's what I propose: Terminology: super function - a function that uses |super| super delegate function - A function which delegates to a super function stored in its [[SuperFunction]] internal property, passing a |super| binding stored in its [[Super]] internal property. so every super function still requires an implicit super parameter Semantics: When a super function is assigned as a method or accessor getter or setter either via an object literal, Object.defineProperty or direct assignment, this would add overhead to every [[Put]]. a test to see if the value is a super function. The way to avoid the test would be defer the test until the value of the property was retrieved (first time only perhaps. This is essentially the same as the dynamic super implementation strategy of conditionally only passing the current object to a super function. So, you have traded overhead on ever call for over head on every [[Put]]. [[Put]]'s probaby occur less often than [[Call]]'s but may still be too much. a super delegate function is created and assigned instead whose [[SuperFunction]] internal property is the super function being assigned, and whose [[Super]] internal property is the [[Prototype]] internal property of the object being assigned to. When a super delegate function is subsequently accessed from an object, e.g. o.methodThatUsesSuper or Object.getOwnPropertyDescriptor(o, accessorWithGetterOrSetterThatUsesSuper), this resolves to the [[TargetFunction]] internal property of the super delegate function. I believe you are saying that upon property revival, a super delegate function reference is replaced with the value of its [[SuperFunction]]. This means there is an additional check (is the property value a super delegate function) on every [[Get]]. When a super function is called directly, e.g. superFunction() or superFunction.call(this), |super| is resolved as the [[Prototype]] internal property of the |this| of the call. How does it know it is being called directly? This seems to be the primary thing this approach adds to simple static super. Why is this really important? Is there a common usage case that requires this. I don't believe it has been addressed by other OO languages. When a super delegate function is called, e.g. o.methodThatUsesSuper(), o.accessorWithGetterThatUsesSuper, or o.accessorWithSetterThatUsesSuper = foo, |super| is bound to the super delegate function's [[Super]] internal property. in other words, the super delegate function calls the super function passing the value of its [[Super]] as the implicit super parameter The equivalence class of a super function in == and === consists of the super function and all of its corresponding super delegate functions, to illustrate: let f = function(){return super.x}, o1 = {method: f}, o2 = Object.create(null, {method: {get: f}}); assert(f === o1.method); // true assert(o1.method === Object.getOwnPropertyDescriptor(o2, method).get); // true Thus, the static super implementation is completely abstracted from the user, and they benefit from the full dynamic super semantics! At the expense of additional runtime
block-lambda revival
Peter Michaux encouraged me to write my thoughts on es-discuss so here I am. Out of the various new function syntaxes proposed ( -, #, {||} ) I have really taken a liking to the block-lambda revival strawman. I think in general they all address similar wishes (e.g. implicit return, this, shorter). I prefer the block-lambda because of some use cases: let a = [1,2,3,4]; let b = a.reduce((a,b) - a + b) // didn't see an example for this one assuming this is how it looks let b = a.reduce {|a,b| a + b} // less cluttery, fn bound by braces, couple characters less to type Self executing function expressions (- { // multi-line }()) {|| // multi-line -- i'm assuming there wouldn't need to be parens around the whole thing because block lambdas are expressions not statements (is this the case with arrow?) }() I like block-lambda because: 1. From my understanding braces aren't going away, lets just embrace them (use them as part of the syntax) 2. Arrow gets too bulky with braces (although worked good for CoffeeScript) 3. Seems to be terse yet clear 4. Encloses the function It may be nice to have side by side comparisons of the different proposals doing the same operation. Anyway, my 2 cents. Thanks! -- Marc (@wavded) ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Making super work outside a literal?
On Jun 23, 2011, at 12:07 PM, Axel Rauschmayer wrote: I believe you about the dynamic super. I can summarize my question as follows: - Making super or current object available to a function incurs costs. These are two separate costs. You didn't define which 'super' but from context I'll assume static. That's a function-creation-time internal property setting cost. If by current object you mean |here|, please use that term for clarity's sake. That has a per-call cost: an extra implicit parameter. We've been over this about five times. Yes, I’m not questioning, just repeating and agreeing. And comparing the cost of |here| to the cost |thisFunction|. Oh, you meant the callee (the called function)? The current object phrase threw me. - Making current function available to a function does not incur costs? This is *not* an extra parameter, then? Do you mean current object, aka |here|? If so, that's an extra parameter. If not, I don't know what you mean. Static super can only be accessed if you have a reference |thisFunction| to the function that is currently executed. I would think that |thisFunction| would also incur costs, but Allen has since said that these costs are negligible. The called function is part of the evaluation results needed to call the function. It is indeed passed on the stack in naive stack-based implementations, to support reflection (e.g. arguments usage by the callee; again remember the caller in general does not know whether the callee uses arguments, debugger, etc.), and debugging (kind of hyper-reflection). In a JIT, the calling convention can use registers and avoid writing to memory more than the minimum needed based on whatever analysis the JIT can support. In any event, there's no extra parameter beyond the situation in today's engines. With dynamic 'super', there is. Related topic: How would |super| be handled if |this| is lexical? There's no connection, AFAIK. But we also don't have a lexical-this proposal in Harmony yet. /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: block-lambda revival
On 23/06/2011, at 23:01, Marc Harter wrote: Peter Michaux encouraged me to write my thoughts on es-discuss so here I am. Out of the various new function syntaxes proposed ( -, #, {||} ) I have really taken a liking to the block-lambda revival strawman. I think in general they all address similar wishes (e.g. implicit return, this, shorter). I prefer the block-lambda because of some use cases: let a = [1,2,3,4]; let b = a.reduce((a,b) - a + b) // didn't see an example for this one assuming this is how it looks let b = a.reduce {|a,b| a + b} // less cluttery, fn bound by braces, couple characters less to type Self executing function expressions (- { // multi-line }()) {|| // multi-line -- i'm assuming there wouldn't need to be parens around the whole thing because block lambdas are expressions not statements (is this the case with arrow?) }() I like block-lambda because: 1. From my understanding braces aren't going away, lets just embrace them (use them as part of the syntax) 2. Arrow gets too bulky with braces (although worked good for CoffeeScript) 3. Seems to be terse yet clear 4. Encloses the function It may be nice to have side by side comparisons of the different proposals doing the same operation. Here: http://jorgechamorro.com/blocks.html Anyway, my 2 cents. Thanks! {|| ... } for shorter *function* syntax is my favorite too. +1(e9) Also, if any { block } could be a lambda, perhaps we won't need that (nor any new) syntax for block-lambdas. Also, I'd prefer to know/see clearly when a function is being call()ed, so I'm not very fond of paren-free calls: foo(bar) is clearly an invocation, unlike foo bar, and readability is more important than saving a few keystrokes. The C language is still (and -ISTM- will be for a long time) important, so -IMO- every little bit of JS's C-like syntax is a plus: less to learn: an old, popular, widely used, well-known, and familiar syntax. JS -unlike other languages- is important enough that it does not need to follow these (dubious) trendy fashions to become popular. Nor to survive. Proper punctuation aids comprehension and we're programming, not writing quick SMSs. -- Jorge.___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Making super work outside a literal?
On Jun 23, 2011, at 2:00 PM, Sean Eagan wrote: Ok, sorry it took me so long, but I agree that the general idea of Object.defineMethod is the best solution. I think it can be improved upon though, by adding the following: Ability to use any property descriptor attributes when defining methods. Ability to assign accessor getters and setters as well which use the assigned to object as |super|. Avoid having to add a defineMethod proxy trap. Great point! That would be something to avoid. Here are the two potential solutions I have in mind for this: * Have Object.defineProperty create new functions with updated [[Super]] when the value, get, or set attributes are functions that use super. This seems enough, you're right. * Add an Object.defineSuperProperty which is the same as the existing Object.defineProperty except that it creates new functions with updated [[Super]] when the value, get, or set attributes are functions that use super. It could potentially throw if no super functions are passed. This one may still require a defineSuperProperty proxy trap, not sure. I'm not sure we need this one. Does it really avoid the proxy trap addition? Also, I wonder if there is a need for a Function.prototype.isSuper similar to Function.prototype.isGenerator? A generator is a kind of function. 'super' evaluates to an object. So this seems misplaced, but first, what use-case does it serve? Can you show an example that motivates it? /be On Thu, Jun 23, 2011 at 10:51 AM, Allen Wirfs-Brock al...@wirfs-brock.com wrote: On Jun 23, 2011, at 3:14 PM, Sean Eagan wrote: On Thu, Jun 23, 2011 at 4:56 AM, Allen Wirfs-Brock al...@wirfs-brock.com wrote: If you don't have actual implementation experience with dynamic languages I think you guys will just have to take Brendan's and my word for this. I now understand and fully agree with your performance arguments against dynamic super. However, I still believe that the dynamic super semantics are largely superior to the static super semantics. Fortunately, I think the dynamic super semantics can be simulated via static super, thus yielding the best of both approaches! Here's what I propose: Terminology: super function - a function that uses |super| super delegate function - A function which delegates to a super function stored in its [[SuperFunction]] internal property, passing a |super| binding stored in its [[Super]] internal property. so every super function still requires an implicit super parameter Semantics: When a super function is assigned as a method or accessor getter or setter either via an object literal, Object.defineProperty or direct assignment, this would add overhead to every [[Put]]. a test to see if the value is a super function. The way to avoid the test would be defer the test until the value of the property was retrieved (first time only perhaps. This is essentially the same as the dynamic super implementation strategy of conditionally only passing the current object to a super function. So, you have traded overhead on ever call for over head on every [[Put]]. [[Put]]'s probaby occur less often than [[Call]]'s but may still be too much. a super delegate function is created and assigned instead whose [[SuperFunction]] internal property is the super function being assigned, and whose [[Super]] internal property is the [[Prototype]] internal property of the object being assigned to. When a super delegate function is subsequently accessed from an object, e.g. o.methodThatUsesSuper or Object.getOwnPropertyDescriptor(o, accessorWithGetterOrSetterThatUsesSuper), this resolves to the [[TargetFunction]] internal property of the super delegate function. I believe you are saying that upon property revival, a super delegate function reference is replaced with the value of its [[SuperFunction]]. This means there is an additional check (is the property value a super delegate function) on every [[Get]]. When a super function is called directly, e.g. superFunction() or superFunction.call(this), |super| is resolved as the [[Prototype]] internal property of the |this| of the call. How does it know it is being called directly? This seems to be the primary thing this approach adds to simple static super. Why is this really important? Is there a common usage case that requires this. I don't believe it has been addressed by other OO languages. When a super delegate function is called, e.g. o.methodThatUsesSuper(), o.accessorWithGetterThatUsesSuper, or o.accessorWithSetterThatUsesSuper = foo, |super| is bound to the super delegate function's [[Super]] internal property. in other words, the super delegate function calls the super function passing the value of its [[Super]] as the implicit super parameter The equivalence class of a super function in == and === consists of the super function and all of its corresponding
Re: block-lambda revival
On Jun 23, 2011, at 3:14 PM, Jorge wrote: Anyway, my 2 cents. Thanks! {|| ... } for shorter *function* syntax is my favorite too. +1(e9) Thanks -- I am continuing to maintain arrow function syntax and block lambda revival as strawmen. Arrows now require only two-token lookahead, ignoring the #!~ prefixes proposed for non-configurable, non-writable, and non-enumerable property assignments in object initialisers. This is in order to support either an object literal body or a braced non-empty block body where the block's first statement is not a labeled statement. Block lambda revival has more grammar changes, but so far they check out. Also, if any { block } could be a lambda, perhaps we won't need that (nor any new) syntax for block-lambdas. We would need new syntax still, for formal parameters. Making blocks be expressions requires unifying the ObjectLiteral and Block productions. I don't know how to do this in without at least two-token lookeahead, and it is not a backward compatible change if done for all places where Statement : Block in the current grammar. Also, I'd prefer to know/see clearly when a function is being call()ed, so I'm not very fond of paren-free calls: foo(bar) is clearly an invocation, unlike foo bar, Your example is too abstracted to be fair. Concretely, the latter will *always* look like foo {|| bar} ... and never foo bar for any bar. and readability is more important than saving a few keystrokes. Readability arguments support the paren-free syntax too. You can't win this by selective arguing. The C language is still (and -ISTM- will be for a long time) important, so -IMO- every little bit of JS's C-like syntax is a plus: less to learn: an old, popular, widely used, well-known, and familiar syntax. C by way of Java, and both are boat anchors. Again, where pray tell is 'function' in C? JS -unlike other languages- is important enough that it does not need to follow these (dubious) trendy fashions to become popular. Nor to survive. Nothing trendy about Smalltalk blocks unless you are a Rubyist. Proper punctuation aids comprehension and we're programming, not writing quick SMSs. This is silly, you're making vague arguments that cut both ways. /be___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: block-lambda revival
I'm liking the block-lambda syntax, I think more than the arrow. One possible shortening could be to exclude the second bar (|) if no argument variables are specified. {| // some block of code }; I don't *think* we'd ever start an expression with a single | operator, so there should be no ambiguity. On 2011-06-23, at 19:27, Brendan Eich wrote: On Jun 23, 2011, at 3:14 PM, Jorge wrote: Anyway, my 2 cents. Thanks! {|| ... } for shorter *function* syntax is my favorite too. +1(e9) Thanks -- I am continuing to maintain arrow function syntax and block lambda revival as strawmen. Arrows now require only two-token lookahead, ignoring the #!~ prefixes proposed for non-configurable, non-writable, and non-enumerable property assignments in object initialisers. This is in order to support either an object literal body or a braced non-empty block body where the block's first statement is not a labeled statement. Block lambda revival has more grammar changes, but so far they check out. Also, if any { block } could be a lambda, perhaps we won't need that (nor any new) syntax for block-lambdas. We would need new syntax still, for formal parameters. Making blocks be expressions requires unifying the ObjectLiteral and Block productions. I don't know how to do this in without at least two-token lookeahead, and it is not a backward compatible change if done for all places where Statement : Block in the current grammar. Also, I'd prefer to know/see clearly when a function is being call()ed, so I'm not very fond of paren-free calls: foo(bar) is clearly an invocation, unlike foo bar, Your example is too abstracted to be fair. Concretely, the latter will *always* look like foo {|| bar} ... and never foo bar for any bar. and readability is more important than saving a few keystrokes. Readability arguments support the paren-free syntax too. You can't win this by selective arguing. The C language is still (and -ISTM- will be for a long time) important, so -IMO- every little bit of JS's C-like syntax is a plus: less to learn: an old, popular, widely used, well-known, and familiar syntax. C by way of Java, and both are boat anchors. Again, where pray tell is 'function' in C? JS -unlike other languages- is important enough that it does not need to follow these (dubious) trendy fashions to become popular. Nor to survive. Nothing trendy about Smalltalk blocks unless you are a Rubyist. Proper punctuation aids comprehension and we're programming, not writing quick SMSs. This is silly, you're making vague arguments that cut both ways. /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: block-lambda revival
[+stay] Another bit of evidence for lexical this, and thus for block lambdas over arrow functions or simple # functions. At http://wiki.ecmascript.org/doku.php?id=harmony:proxiesrev=1308499413do=diff I corrected a bug noticed just now by Mike Stay (cc'ed). The bug dates from http://wiki.ecmascript.org/doku.php?id=harmony:proxiesrev=1280925681#trap_defaults in August of 2010, more than 10 months ago, when Tom van Cutsem wrote: keys: function() { return this.getOwnPropertyNames().filter( function (name) { return this.getOwnPropertyDescriptor(name).enumerable }); } This bug was written by one of the most talented and careful programmers I have ever met. The page it appears in has been carefully examined by many experts (not just official experts -- real experts) and a large community of talented people, guiding at least three implementations of the proxy system. The bug was only noticed by Mike Stay while starting the newest of these implementation efforts, emulating proxies on old browsers within ES5/3. Most JavaScript code will be much less reviewed than this, and by a community less expert than ourselves in the peculiarities of the language. We need to ask, why was this bug so easy for an expert to make and so hard for a community of experts to catch? My guess is that it's because Array.prototype.filter feels like a control structure and the anon function we're calling it with feels like a control structure block, even though we know that we know otherwise. When we see the this inside and try to figure out what it means, we search outward for a function and our eye often catches the wrong one. Let's see what Tom might have written with three of the other function-like proposals. To be fair, I'll assume here that return can be omitted in all. Arrow functions: keys: function() { return this.getOwnPropertyNames().filter( (name) - this.getOwnPropertyDescriptor(name).enumerable ); } Simple # functions keys: function() { return this.getOwnPropertyNames().filter( #(name) { this.getOwnPropertyDescriptor(name).enumerable }); } block lambdas keys: function() { return this.getOwnPropertyNames().filter { |name| this.getOwnPropertyDescriptor(name).enumerable } } The first and obvious point I'm making is that Tom would not have made this particular mistake with block lambdas, but I recognize that this argument cuts both ways. Other uses may have the opposite hazard, and we need to examine this as well. The new point is that arrow functions and simple # functions make the original hazard *worse*, because, like function, they still rebind this. But by being less verbose than function, it's even easier for the eye to miss them when seeing a this in their body and trying to determine what it means. The more I think about this, the more I'm inclined to believe that, for functions which rebind this, the verbosity of function is a virtue. As fallible programmers, we can only afford a more compact function notation for defining functions with lexical this -- like block lambdas. -- Cheers, --MarkM ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: block-lambda revival
On Jun 23, 2011, at 5:16 PM, Mark S. Miller wrote: Arrow functions: keys: function() { return this.getOwnPropertyNames().filter( (name) - this.getOwnPropertyDescriptor(name).enumerable ); } This is not giving arrow functions their due, though. Just use = and all is well. Yes, you have to remember, but as you note, the which this? cuts both ways. Simple # functions keys: function() { return this.getOwnPropertyNames().filter( #(name) { this.getOwnPropertyDescriptor(name).enumerable }); } Aside: we don't have a live proposal to use # this way, with completion value as implicit return value. Full 'return' or something like the empty-label hack I sketched (prefix : followed by tail position) would be needed. Also, # for frozen value counterparts (records and tuples, as well as functions) seems a better use of that character. block lambdas keys: function() { return this.getOwnPropertyNames().filter { |name| this.getOwnPropertyDescriptor(name).enumerable } } The first and obvious point I'm making is that Tom would not have made this particular mistake with block lambdas, but I recognize that this argument cuts both ways. Other uses may have the opposite hazard, and we need to examine this as well. The new point is that arrow functions and simple # functions make the original hazard *worse*, because, like function, they still rebind this. While - does bind |this| dynamically, = does not. But by being less verbose than function, it's even easier for the eye to miss them when seeing a this in their body and trying to determine what it means. You're right that it's easy to miss the bad thing, even if there's a good thing. The more I think about this, the more I'm inclined to believe that, for functions which rebind this, the verbosity of function is a virtue. But it didn't help in the story you led with :-). As fallible programmers, we can only afford a more compact function notation for defining functions with lexical this -- like block lambdas. Or =? I'm leaning toward block lambdas myself, but I feel the need to play umpire here a bit -- arrows need fair play including =, not just -. /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: block-lambda revival
On Jun 23, 2011, at 7:04 PM, Mike Samuel wrote: 2011/6/23 Brendan Eich bren...@mozilla.com: On Jun 23, 2011, at 4:07 PM, Rob Campbell wrote: I'm liking the block-lambda syntax, I think more than the arrow. One possible shortening could be to exclude the second bar (|) if no argument variables are specified. {| // some block of code }; This does not work. How do you know that what follows the first | is not a formal parameter? Formal parameter syntax looks like expression syntax -- currently Expression covers FormalParameterList. Arrow function syntax relies on this. /be Are you thinking that {| a | a } it is ambiguous whether function (a) { return a; } or function () { return a | a; } Precisely. /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Making super work outside a literal?
On Jun 23, 2011, at 10:39 PM, Axel Rauschmayer wrote: Related topic: How would |super| be handled if |this| is lexical? There's no connection, AFAIK. But we also don't have a lexical-this proposal in Harmony yet. I would expect problems that are similar to that = this: var obj = { mymethod: function(arr) { arr.forEach(function(x) { super.mymethod(x); // would this work? }); } } From http://wiki.ecmascript.org/doku.php?id=harmony:object_initialiser_super : PrimaryExpression : ... super The value of super is the same as the value of this but when super is used as the base of a property access the property lookup starts with the object that is the prototype of the object defined by the object literal that contains the reference to super. This seems to say that the wrong |this| would be used, with the right property lookup starting point. Yikes. Thankfully, I don’t think that super-calls will be very common in typical JavaScript code, in contrast to this. Not sure this will all pan out as proposed. 'super' in classes works. 'super' in any function nested however deeply in an object initialiser, perhaps not. If the above spec constrained the nesting to be immedate method of, that would seem to solve it. But would that be too restrictive? /be -- Dr. Axel Rauschmayer a...@rauschma.de twitter.com/rauschma home: rauschma.de blog: 2ality.com ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss