Re: [[Extensible]]and Proxies (Was: Proxy.isProxy )
Le 18/07/2011 02:58, Brendan Eich a écrit : On Jul 17, 2011, at 3:14 PM, David Bruant wrote: I'm only catching up now on this thread. I have indeed started some work to try to implement an invariant-enforcing implementation of preventExtension rather than fix+become. Just off the top of my head, sorry if I'm misunderstanding: fix based on become is very attractive to VM implementors, because it avoids extra for-life-of-fixed-object costs in the proxy or native code paths that aren't already there with Proxies as proposed and with ES5. "becomes" lets the brain-swapped objects do what they do best. There's no hybridization as if by composition using vtbls, if statements, or whatever, that affects proxy codepaths. It's true one could "become" a proxy with the extra code paths, but that's duplicative at some level (not necessarily code inlining, but layering the extra preventExtensions enforcement somehow). If the preventExtensions enforcement is the same as the pre-fix overhead for fixed properties, perhaps this is less of an issue. I was in doubt for some time and I realized that the fixed properties required very few checks and these checks turn out to be the same performed by some code defined in a way or another in one of the ES5.1 8.12 algorithms. By enforcing ES5 invariants, we actually are trying to enforce things that are enforced for normal objects already. If you haven't read Tom's FixedHandler implementation [1], I encourage you to, because the minimalism of it is quite interesting. Especially, in the get{Own}PropertyDescriptor and defineProperty traps, Object.defineProperty is called and as commented, the purpose is to (re)use the argument-checking code of this method to let it throw if needed. It's extremely generic and future-proof with regard to new sorts of property descriptors. We'll see what the implementation of invariant checking non-extensible proxies looks like. Sticking with becomes, but (a) distinguishing preventExtensions from seal from freeze and (b) allowing the [[Class]] or ES.next equivalent to be controlled would allow, e.g., Proxy-emulated Arrays that fix to become real (pE'ed, sealed, or frozen) Arrays. Distinguishing preventExtensions from seal from freeze will still be possible with an invariant-enforcing proxy if that's the chosen solution. David [1] http://code.google.com/p/es-lab/source/browse/trunk/src/proxies/FixedHandler.js ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: [[Extensible]]and Proxies (Was: Proxy.isProxy )
Le 18/07/2011 03:03, Brendan Eich a écrit : On Jul 17, 2011, at 6:01 PM, Brendan Eich wrote: See my previous reply, about fixed properties, the fix trap, controlling the [[Class]] of the object the proxy "becomes" upon fixing, and preserving the (preventExtensions, seal, freeze) distinction when fixing. Here's a possibly dumb question: why not have fix return not a pdmap for the new Object instance to be fixed at those properties, rather an object (of any class) to be fixed by the exact cause of the fix trap: preventExtensions, seal, or freeze? Interesting. What would happen if the returned object is a proxy? Regardless, my understanding of the fix+become design was to enforce the non-extensibility invariants (and if I'm missing something, please tell me so). I have to admit that it really feels weird to me that because I call Object.{preventExtensions|seal|freeze}, my handler should stop its existence and my proxy should "become" another object (even a native array or a DOM Node if we want to allow that). It sounds very arbitrary to me. Why does preventing extension should throw my loggers away? Or stop the forwarding of all operations to the target object if I defined a forwarder? Actually, I've raised the issue in another thread that a forwarder proxy cannot properly forward Object.{preventExtensions|seal|freeze} to the target object because within the fix trap, there is no way to distinguish between the 3. David ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
An "extend" operator is a natural companion to <|
I've recently been experimenting with coding both prototypal and class based object definitions using the various syntactic forms that are currently on the table. Something has emerged from that which has surprised me. I have never been a big fan of the "extend" method that is provided by a number of JavaScript frameworks. However, based upon my experiments I'm now think that something like extend solves several issues with the declarative object and class declarations that we have recently been discussing. Just in case there is anyone on this list who isn't familiar with extend, here is a quick explanation. In most frameworks that support it, "extend" is invoked something like this: obj.extend(extensionObj) It copies the own properties of extensionObj and makes corresponding own properties for obj. Exact details about which properties are copied, various error conditions and even the name of the method varies among frameworks. It is generally has been used as an imperative supplement to ECMASript's built-in prototype inheritance, for example to provide effects similar to multiple inheritance. The use cases that interests me are some what different then this current common use. But first, I'll cut to the chase. Here is a quick summary of what I'm going to propose: In addition to <| we need another operator <& that is similar to the "extend" method in various frameworks. It replicates the own properties of its RHS operation on its LHS operand. It provides an easy declarative way to describe the properties that need to be added to an already created object. For example: obj <& { __constDataProp: x, method(a) {return a+this._constDataProp}, get konst() {return this.__costDataProp} }; adds three properties to obj. So, on to the use cases that motivates this. In https://mail.mozilla.org/pipermail/es-discuss/2011-July/015792.html I used an prototypal inheritance pattern in an example. A slightly simplified version of the example is: 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 } }; } In this pattern, the "new" method on a prototype object is used to create instance of the corresponding object abstraction. It does this by using the <| operator to create the instance as an object whose [[Protoype]] is set to the prototypal instance. The object literal on the right of the <| lists the per instance state of the new object. In this case the properties named "__x" and "__y". This is a nice declarative way to describe the per instance state but it turns out it doesn't generalize very well to multiple levels of inheritance. If you tried to use this pattern to create a three dimensional point, it would probably look something like: const Point3D = Point <| { //private members __z: 0, //public members new(x,y,z) { if (!this.__validate(x,y) || typeof z != 'number') throw "invalid"; return this <| { __x: x, __y: y, __z: z } }; } Note that the "new" method in Point3D had to essentially copy everything that was in the "new" method of Point. This isn't very good use of inheritance. It also may not work if true private properties are used instead of just a naming convention. What you would really like to do is to let the implementation of "new" in Point do all the work that relates to x and y and only have to include in Point3D code that relates to z. You might be tempted to write it as: const Point3D = Point <| { //private members __z: 0, //public members new(x,y,z) { if (typeof z != 'number') throw "invalid"; return super.new(x,y) <| { __z: z } }; } However, that wouldn't create what was probably intended. Instead of creating a single instance object that inherits from Point3D it creates two objects, the first one has Point3D as its [[Prototype]] and has own properties "__x" and "__y". The second object has the first object as its [[Prototype]] and as "__z" as an own property. If a Point4D was created following the same pattern the per instance state of create by each call of Point4D would be spread over three objects. The problem is that we want to do a super.new call at each level of the inheritance hierarchy to ensure that we do all the necessary initialization without unnecessary code copying. However, each level is doing a <| which creates a distinct object. Instead, what we really want to do is create a single object at the top of the inheritance hierarchy and then add additional properties to that object at each inheritance level. If we want to
Re: [[Extensible]]and Proxies (Was: Proxy.isProxy )
On Jul 18, 2011, at 2:35 AM, David Bruant wrote: > Le 18/07/2011 03:03, Brendan Eich a écrit : >> On Jul 17, 2011, at 6:01 PM, Brendan Eich wrote: >> >>> See my previous reply, about fixed properties, the fix trap, controlling >>> the [[Class]] of the object the proxy "becomes" upon fixing, and preserving >>> the (preventExtensions, seal, freeze) distinction when fixing. >> Here's a possibly dumb question: why not have fix return not a pdmap for the >> new Object instance to be fixed at those properties, rather an object (of >> any class) to be fixed by the exact cause of the fix trap: >> preventExtensions, seal, or freeze? > Interesting. What would happen if the returned object is a proxy? Good question. First impulse is to make that an error, but if proxies can be fixed, then the possibility of a fixed and inextensible proxy sufficing as return value of this hypothetical fix trap is plausible. We would need continued, VM-level enforcement that such a fixed proxy cannot be extended, and of course that all of its properties are fixed. > Regardless, my understanding of the fix+become design was to enforce the > non-extensibility invariants (and if I'm missing something, please tell me > so). I have to admit that it really feels weird to me that because I call > Object.{preventExtensions|seal|freeze}, my handler should stop its existence > and my proxy should "become" another object (even a native array or a DOM > Node if we want to allow that). It sounds very arbitrary to me. The original proxy design had no support for fixed properties, so no way to let the handler intercede after fix without leaving too much bug/attack surface. If we extend proxies to support fixed properties and ![[Extensible]], then we may be able to relax the design. But nothing here is truly arbitrary. It's a divide-and-conquer progression, I think. Also, to repeat: implementation of becomes with a newborn is really quite slick and relatively easy to support, compared to more invasive fixed/inextensible enforcement. > Why does preventing extension should throw my loggers away? Or stop the > forwarding of all operations to the target object if I defined a forwarder? Because there was no way in sight to enforce the critical invariants. Possibly there is, now. > Actually, I've raised the issue in another thread that a forwarder proxy > cannot properly forward Object.{preventExtensions|seal|freeze} to the target > object because within the fix trap, there is no way to distinguish between > the 3. Yes indeed. /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Array.prototype.concat result length (ES5.1)
On 07/14/2011 10:04 AM, Allen Wirfs-Brock wrote: It is probably a bug, because array index based operations generally warp around to 0 at 2^32. Freudian slip? :-D Easiest fix is to just add the length-set to concat. For a quick ES5 erratum that seems best to me. Removing all the RangeError stuff, and making array indexes just non-negative integers, would be nice for ES6 or similar. I suspect changing that won't break anyone worth caring about, although I do know some people have taken the time to care about this in the past (mostly in a spec-nut way :-) ): http://hexmen.com/blog/2006/12/push-and-pop/ Without having thought too hard about exactly what would be involved, I suspect the amount of stuff you'd need to adjust, and the complexity of checking for sane behavior in all cases (including some of the 2**52 upper-bounding edge cases, depending on what new semantics you might want for "array indexes" or whatever), would make it unwise to try for ES5 errata. But I could well be wrong about this, so I wouldn't necessarily write off that possibility. Jeff ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: [[Extensible]]and Proxies (Was: Proxy.isProxy )
On Jul 18, 2011, at 2:06 AM, David Bruant wrote: > By enforcing ES5 invariants, we actually are trying to enforce things that > are enforced for normal objects already. If you haven't read Tom's > FixedHandler implementation [1], I encourage you to, because the minimalism > of it is quite interesting. Read it, very slick. Has it changed recently? > Especially, in the get{Own}PropertyDescriptor and defineProperty traps, > Object.defineProperty is called and as commented, the purpose is to (re)use > the argument-checking code of this method to let it throw if needed. It's > extremely generic and future-proof with regard to new sorts of property > descriptors. That is a good sign, and anything else would be a "bad sign" ;-). > We'll see what the implementation of invariant checking non-extensible > proxies looks like. This is the crucial part. >> Sticking with becomes, but (a) distinguishing preventExtensions from seal >> from freeze and (b) allowing the [[Class]] or ES.next equivalent to be >> controlled would allow, e.g., Proxy-emulated Arrays that fix to become real >> (pE'ed, sealed, or frozen) Arrays. > Distinguishing preventExtensions from seal from freeze will still be possible > with an invariant-enforcing proxy if that's the chosen solution. Is there a proposal? /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Array.prototype.concat result length (ES5.1)
On Jul 18, 2011, at 10:51 AM, Jeff Walden wrote: > > Easiest fix is to just add the length-set to concat. For a quick ES5 erratum > that seems best to me. Yes, this needs to be in the errata. > > Removing all the RangeError stuff, and making array indexes just non-negative > integers, would be nice for ES6 or similar. I suspect changing that won't > break anyone worth caring about, although I do know some people have taken > the time to care about this in the past (mostly in a spec-nut way :-) ): > > http://hexmen.com/blog/2006/12/push-and-pop/ > > Without having thought too hard about exactly what would be involved, I > suspect the amount of stuff you'd need to adjust, and the complexity of > checking for sane behavior in all cases (including some of the 2**52 > upper-bounding edge cases, depending on what new semantics you might want for > "array indexes" or whatever), would make it unwise to try for ES5 errata. > But I could well be wrong about this, so I wouldn't necessarily write off > that possibility. We won't do this as an errata. It is a change that would have to be treated as a change in a future edition rather than an error correction of the current edition. Allen ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: An "extend" operator is a natural companion to <|
It would also allow declarative non-integer own properties for arrays, and arbitrary own properties to regular expressions, numbers, booleans, and strings, though I can't think of any specific use cases for those off of the top of my head. Also, how about |> as opposed to <&, since it is a dual to <| adding own rather than inherited properties? On Mon, Jul 18, 2011 at 12:32 PM, Allen Wirfs-Brock wrote: > I've recently been experimenting with coding both prototypal and class based > object definitions using the various syntactic forms that are currently on > the table. Something has emerged from that which has surprised me. I have > never been a big fan of the "extend" method that is provided by a number of > JavaScript frameworks. However, based upon my experiments I'm now think that > something like extend solves several issues with the declarative object and > class declarations that we have recently been discussing. > Just in case there is anyone on this list who isn't familiar with extend, > here is a quick explanation. In most frameworks that support it, "extend" is > invoked something like this: > obj.extend(extensionObj) > It copies the own properties of extensionObj and makes corresponding own > properties for obj. Exact details about which properties are copied, > various error conditions and even the name of the method varies among > frameworks. It is generally has been used as an imperative supplement to > ECMASript's built-in prototype inheritance, for example to provide effects > similar to multiple inheritance. > The use cases that interests me are some what different then this current > common use. > But first, I'll cut to the chase. Here is a quick summary of what I'm going > to propose: > In addition to <| we need another operator <& that is similar to the > "extend" method in various frameworks. It replicates the own properties of > its RHS operation on its LHS operand. It provides an easy declarative way > to describe the properties that need to be added to an already created > object. For example: > > obj <& { > __constDataProp: x, > method(a) {return a+this._constDataProp}, > get konst() {return this.__costDataProp} > }; > > adds three properties to obj. > > So, on to the use cases that motivates this. In > https://mail.mozilla.org/pipermail/es-discuss/2011-July/015792.html I used > an prototypal inheritance pattern in an example. A slightly simplified > version of the example is: > > 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 > } > }; > } > > In this pattern, the "new" method on a prototype object is used to create > instance of the corresponding object abstraction. It does this by using the > <| operator to create the instance as an object whose [[Protoype]] is set to > the prototypal instance. The object literal on the right of the <| lists > the per instance state of the new object. In this case the properties named > "__x" and "__y". This is a nice declarative way to describe the per > instance state but it turns out it doesn't generalize very well to multiple > levels of inheritance. If you tried to use this pattern to create a three > dimensional point, it would probably look something like: > > const Point3D = Point <| { > //private members > __z: 0, > //public members > new(x,y,z) { > if (!this.__validate(x,y) || typeof z != 'number') throw > "invalid"; > return this <| { > __x: x, > __y: y, > __z: z > } > }; > } > > Note that the "new" method in Point3D had to essentially copy everything > that was in the "new" method of Point. This isn't very good use of > inheritance. It also may not work if true private properties are used > instead of just a naming convention. What you would really like to do is to > let the implementation of "new" in Point do all the work that relates to x > and y and only have to include in Point3D code that relates to z. You might > be tempted to write it as: > > const Point3D = Point <| { > //private members > __z: 0, > //public members > new(x,y,z) { > if (typeof z != 'number') throw "invalid"; > return super.new(x,y) <| { > __z: z > } > }; > } > > However, that wouldn't create what was probably intended. Instead of > creating a single instance object that inherits from Point3D it creates two > objects, the first one has Point3D as its [[Prototype]] and has own > properties "__x" and "__y". The second object has the first object as its > [[Prototype]] and as "__z" as an own property. If a Point4D was created > followin
Re: An "extend" operator is a natural companion to <|
Hawt. A bit rough in that LHS <& RHS mutates LHS, whereas LHS <| RHS is pure and produces a new object (which could be optimized to mutate RHS, note well!). Both <| and <& are operators, to support chaining. Would it be better for <& to be pure as <| is, and make an assignment operator form, LHS <&= RHS, that expands to LHS = LHS <& RHS? Anyway, if I have <& right, then: class SkinnedMesh extends THREE.Mesh { constructor(geometry, materials) { super(geometry, materials); public identityMatrix = new THREE.Matrix4(); public bones = []; public boneMatrices = []; ... } update(camera) { ... super.update(); } } from http://wiki.ecmascript.org/doku.php?id=harmony:classes would desugar to: function SkinnedMesh(geometry, materials) { return super(geometry, materials) <& { identityMatrix: new THREE.Matrix4(), bones: [], boneMatrices: [], ... }; } SkinnedMesh.prototype = THREE.Mesh.prototype <| { update(camera) { ... super.update(); } }; Class-side inheritance could be done via let SkinnedMesh = THREE.Mesh <| (function (geom, mats) { ... } <& { classMethod() {...} }); This loses the hoisting of SkinnedMesh that the function declaration desugaring gained "for free" -- another rough spot to smooth out. There's still a usability argument for class syntax, certainly. Class syntax is also, for some folks, an attractive nuisance because of single-inheritance OOP being oversold and very often the wrong paradigm. But <& helps there too -- one can more conveniently make mixins (I must mean <&= here, of course). The last conflicting name wins, or so it seems from what you've written. The compositional answer there is to seal properties you don't want anyone to redefine by a conflicting extend operation. I'm assuming the internal method used to update the LHS or populate the new copy of it is [[DefineOwnProperty]], as with ES5 object literals, and not [[Put]]. /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: An "extend" operator is a natural companion to <|
On Jul 18, 2011, at 11:29 AM, Sean Eagan wrote: > It would also allow declarative non-integer own properties for arrays, > and arbitrary own properties to regular expressions, numbers, > booleans, and strings, though I can't think of any specific use cases > for those off of the top of my head. Yes, I should have mentioned at least the array case. > > Also, how about |> as opposed to <&, since it is a dual to <| adding > own rather than inherited properties? I'd be a bit concerned about some people getting confused about which direction of the "arrow" corresponds to each operation. Might be particularly hard for people with dyslexia. I have played around with some other possibilities, for example, +<| . I like the nemonic value of having + or & as part of the operator symbol for "extend" but unfortunately <+ can't be used. Allen > > On Mon, Jul 18, 2011 at 12:32 PM, Allen Wirfs-Brock > wrote: >> I've recently been experimenting with coding both prototypal and class based >> object definitions using the various syntactic forms that are currently on >> the table. Something has emerged from that which has surprised me. I have >> never been a big fan of the "extend" method that is provided by a number of >> JavaScript frameworks. However, based upon my experiments I'm now think that >> something like extend solves several issues with the declarative object and >> class declarations that we have recently been discussing. >> Just in case there is anyone on this list who isn't familiar with extend, >> here is a quick explanation. In most frameworks that support it, "extend" is >> invoked something like this: >>obj.extend(extensionObj) >> It copies the own properties of extensionObj and makes corresponding own >> properties for obj. Exact details about which properties are copied, >> various error conditions and even the name of the method varies among >> frameworks. It is generally has been used as an imperative supplement to >> ECMASript's built-in prototype inheritance, for example to provide effects >> similar to multiple inheritance. >> The use cases that interests me are some what different then this current >> common use. >> But first, I'll cut to the chase. Here is a quick summary of what I'm going >> to propose: >> In addition to <| we need another operator <& that is similar to the >> "extend" method in various frameworks. It replicates the own properties of >> its RHS operation on its LHS operand. It provides an easy declarative way >> to describe the properties that need to be added to an already created >> object. For example: >> >> obj <& { >>__constDataProp: x, >>method(a) {return a+this._constDataProp}, >>get konst() {return this.__costDataProp} >> }; >> >> adds three properties to obj. >> >> So, on to the use cases that motivates this. In >> https://mail.mozilla.org/pipermail/es-discuss/2011-July/015792.html I used >> an prototypal inheritance pattern in an example. A slightly simplified >> version of the example is: >> >> 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 >> } >> }; >> } >> >> In this pattern, the "new" method on a prototype object is used to create >> instance of the corresponding object abstraction. It does this by using the >> <| operator to create the instance as an object whose [[Protoype]] is set to >> the prototypal instance. The object literal on the right of the <| lists >> the per instance state of the new object. In this case the properties named >> "__x" and "__y". This is a nice declarative way to describe the per >> instance state but it turns out it doesn't generalize very well to multiple >> levels of inheritance. If you tried to use this pattern to create a three >> dimensional point, it would probably look something like: >> >> const Point3D = Point <| { >> //private members >> __z: 0, >> //public members >> new(x,y,z) { >> if (!this.__validate(x,y) || typeof z != 'number') throw >> "invalid"; >> return this <| { >> __x: x, >> __y: y, >> __z: z >> } >> }; >> } >> >> Note that the "new" method in Point3D had to essentially copy everything >> that was in the "new" method of Point. This isn't very good use of >> inheritance. It also may not work if true private properties are used >> instead of just a naming convention. What you would really like to do is to >> let the implementation of "new" in Point do all the work that relates to x >> and y and only have to include in Point3D code that relates to z. You might >> be tempted to write it as: >> >> const Point3D = Point <| { >>
Re: An "extend" operator is a natural companion to <|
Le 18/07/2011 19:32, Allen Wirfs-Brock a écrit : > I've recently been experimenting with coding both prototypal and class > based object definitions using the various syntactic forms that are > currently on the table. Something has emerged from that which has > surprised me. I have never been a big fan of the "extend" method that > is provided by a number of JavaScript frameworks. However, based upon > my experiments I'm now think that something like extend solves several > issues with the declarative object and class declarations that we have > recently been discussing. > > Just in case there is anyone on this list who isn't familiar with > extend, here is a quick explanation. In most frameworks that support > it, "extend" is invoked something like this: >obj.extend(extensionObj) > It copies the own properties of extensionObj and makes corresponding > own properties for obj. Very recently, I have read jQuery source code of jQuery.extend and I have been surprised to discover that actually not only own properties are copied. See definition at [1] and the main for-in loop between lines 334 and 360. Do anyone has an idea of why it is like this? > (...) > > So, why use an operator like <& instead of a method named "extend": > 1) A big reason is that frameworks already use the extend name and > the exact semantics we would define for it are only to match the > current semantics of these frameworks. By not using that name we > avoid compatibility issues. > 2) A operator form such as <& avoids issue of method redefinition and > can more easily added to existing syntactic forms such as function > declarations. > 3) It is conceptually natural a natural companion to <|. It makes > sense to learn about the two operators together (particularly with > regard to object literals on the RHS). 4) It makes static analysis easier. I'm currently working on some JavaScript analysis, trying to extract out the API of a JS library. Plenty of cases are easy. However, jQuery, defines its API in literal objects which "extend" jQuery (see [2], for instance) or jQuery.prototype (jQuery.fn). Doing a static analysis which will be able to figure this one out will be extremely hard at the very best and most likely impossible at all (because it will require my static analysis to understand jQuery.extend semantics, which sounds impossible to me for now) Having some syntax for that will make the analysis I want to do not only possible, but easy. Having a standardized "extend" method would make the static analysis possible with the extra burden of making sure it has not been overridden which is impossible in some cases (think obj[var1 + var2] = 1;). On a related note, as I said, I'm working on extracting a "JavaScript API" out of a JS file. For that very purpose, I cannot think of something more useful than a class syntax. Overall, I am highly in favor of language constructs that make Javascript more suitable for reliable and easy machine understandability as it will allow to extract more useful information just based on the source code. > I think <& is a natural companion to <| and is in line with our goals > to both enhancing object literals and providing a class declarations > as alternative to stand-alone constructor functions. It increases the > expressiveness of object literals for declaratively defining > prototypal object models and permits simplifications of the proposed > class declaration form. Indeed. I really like this proposal. David [1] https://github.com/jquery/jquery/blob/master/src/core.js#L304 [2] https://github.com/jquery/jquery/blob/master/src/core.js#L368 ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: An "extend" operator is a natural companion to <|
On Jul 18, 2011, at 11:32 AM, Brendan Eich wrote: > Hawt. > > A bit rough in that LHS <& RHS mutates LHS, whereas LHS <| RHS is pure and > produces a new object (which could be optimized to mutate RHS, note well!). > Both <| and <& are operators, to support chaining. Would it be better for <& > to be pure as <| is, and make an assignment operator form, LHS <&= RHS, that > expands to LHS = LHS <& RHS? One way to think about <& is as a more declarative and more concise alternative to Object.defineProperties. I think there are plenty of use causes for the mutating extends. For example, adding properties to the prototype object of a function. Another issue is that the object you need to extend may already have been captured somewhere. For example, a super.constructor call might have registered the object in a WeakMap and if <& created a new instance you would loose the identify relationship. In practice, I think most of the more declarative uses such as adding own properties can be implemented (and perhaps even specified) such that no extra objects need to be created. One thing that I think is still open is whether the RHS needs to be an ObjectLiteral or whether any object producing expression should be allowed. All the use cases I have explored use an ObjectLiteral but I don't see any hazard (like would be the case if <| mutated the LHS [[Prototype]]) with allowing a non-literal value of the RHS. > > Anyway, if I have <& right, then: > > class SkinnedMesh extends THREE.Mesh { > constructor(geometry, materials) { >super(geometry, materials); > >public identityMatrix = new THREE.Matrix4(); >public bones = []; >public boneMatrices = []; >... > } > > update(camera) { >... >super.update(); > } > } > > from http://wiki.ecmascript.org/doku.php?id=harmony:classes would desugar to: > > function SkinnedMesh(geometry, materials) { > return super(geometry, materials) <& { >identityMatrix: new THREE.Matrix4(), >bones: [], >boneMatrices: [], >... > }; > } > > SkinnedMesh.prototype = THREE.Mesh.prototype <| { > update(camera) { >... >super.update(); > } > }; yes, plus you need a constructor: SkinnedMesh in the object literal used to defined SkinnedMesh.prototype > > Class-side inheritance could be done via > > let SkinnedMesh = THREE.Mesh <| (function (geom, mats) { ... } <& { > classMethod() {...} }); > > This loses the hoisting of SkinnedMesh that the function declaration > desugaring gained "for free" -- another rough spot to smooth out. > > There's still a usability argument for class syntax, certainly. Perhaps, but a simpler class syntax is arguably a better class syntax and as I mentioned "static" properties are relatively rare. If the class declaration is taking care of building both the class and instance side prototype chains then class SkinnedMesh extends THREE.Mesh { ... } <& { classMethod() {...} }; would take care of them without complicating the class declaration body. > > Class syntax is also, for some folks, an attractive nuisance because of > single-inheritance OOP being oversold and very often the wrong paradigm. But > <& helps there too -- one can more conveniently make mixins (I must mean <&= > here, of course). > > The last conflicting name wins, or so it seems from what you've written. The > compositional answer there is to seal properties you don't want anyone to > redefine by a conflicting extend operation. I'm assuming the internal method > used to update the LHS or populate the new copy of it is > [[DefineOwnProperty]], as with ES5 object literals, and not [[Put]]. Exactly. Like I mentioned above, <& is I proposed it (your <&=) is essentially a syntactic shorthand for Object.defineProperties. Allen ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: An "extend" operator is a natural companion to <|
On Mon, Jul 18, 2011 at 10:32 AM, Allen Wirfs-Brock wrote: > This is a nice declarative way to describe the per instance state but it turns out it doesn't generalize very well to multiple levels of inheritance. This is an important point. I think the reason most OOP languages make a distinction between object construction and initialization is because in the presence of inheritance, you need 1 construction, but N initializations where N is the depth of your inheritance chain. If you try to construct and initialize in one step, it's hard to do that (as your proposal addresses). > A similar issue exists for the proposed class declarations. That proposal includes the concept of "static" (a lot of us don't like that the term "static" in this context) property declaration: Yeah, I don't think anyone is crazy about that keyword, but it's: 1. Reserved already. 2. Familiar from other languages and it works about the same here as it does in those. I liked "class" instead, but the worry about nested classes was enough to talk me out of it. We have a challenge with JS keywords: 1. We want to re-use keywords from other languages to make JS familiar and to use what's already in the programmer's head. If some JS construct looks and acts similar to a construct in other popular languages, using the same word for it is like instant knowledge. 2. We want to emphasize JS's unique features. JS is not Java (or C++, or C#, or Smalltalk, or Ruby) and we run the risk of leading users down a false path if we make JS look superficially too much like them. There's also a strong cultural bias where JS = light, terse, expressive, fun and Java = verbose, rigid, work. Borrowing keywords from Java makes people worry that there's going to be a JavaScript khaki dress code. Personally, I think that's much ado about nothing, but I can understand why people would worry that the suits are going to show up and crash the party. > Static properties are really just own properties of the constructor object. While sometimes useful, they occur relatively infrequently yet they require a additional declaration form within class bodies. I just did some quick hunting through a few classes in the Closure library. Instance methods are definitely the most common kind of member there, but static methods were used in every type I looked at. For example, goog.ui.Component has a static mutable field, three "constants" which are properties on the constructor, and a couple of static methods. goog.ui.Tooltip has five members that would use "static" in the class proposal. > This complicates the conceptual model of a class declaration by allowing intermingling of constructor and prototype property declaration. There can also be confusion between what goes on the prototype and what goes on the new instance. > This also increase the potential for confusion about the meaning of "this" (and "super") within such static property declarations. Good point. From a conceptual simplicity argument, I like the idea of having different curly blocks for "stuff on the ctor" and "stuff on the proto". Pragmatically, though, C++, Java, and C# all mix instance and non-instance members together and people seem to get by without too much trouble. Looking at your example: class Point { private __validate(x,y) { return typeof x == 'number' && typeof y = 'number'}; constructor(x,y) { if (!this.__validate(x,y)) throw "invalid"; return this <& { private __x: x, private __y: y }; } } <& { get origin() { new this(0,0); } }; A few things stand out for me: 1. Personal preference, but it's pretty punctuation heavy. 2. It seems strange that the instance methods are in the class block, and the methods on the class itself aren't. That feels backwards. 3. Unless I've had it explained to me, there's no way I could figure out what that code means even if I'm an expert in other programming languages. - bob ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: An "extend" operator is a natural companion to <|
On Jul 18, 2011, at 12:16 PM, Allen Wirfs-Brock wrote: > On Jul 18, 2011, at 11:32 AM, Brendan Eich wrote: > >> Hawt. >> >> A bit rough in that LHS <& RHS mutates LHS, whereas LHS <| RHS is pure and >> produces a new object (which could be optimized to mutate RHS, note well!). >> Both <| and <& are operators, to support chaining. Would it be better for <& >> to be pure as <| is, and make an assignment operator form, LHS <&= RHS, that >> expands to LHS = LHS <& RHS? > > One way to think about <& is as a more declarative and more concise > alternative to Object.defineProperties. I think there are plenty of use > causes for the mutating extends. For example, adding properties to the > prototype object of a function. Another issue is that the object you need to > extend may already have been captured somewhere. For example, a > super.constructor call might have registered the object in a WeakMap and if > <& created a new instance you would loose the identify relationship. Sure, I'm not saying word one against the mutating form -- just noting the symmetry break w.r.t. <| and suggesting a fix that builds on the assignment operator. > In practice, I think most of the more declarative uses such as adding own > properties can be implemented (and perhaps even specified) such that no extra > objects need to be created. Agreed. > One thing that I think is still open is whether the RHS needs to be an > ObjectLiteral or whether any object producing expression should be allowed. > All the use cases I have explored use an ObjectLiteral but I don't see any > hazard (like would be the case if <| mutated the LHS [[Prototype]]) with > allowing a non-literal value of the RHS. Probably right; another symmetry break. /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: An "extend" operator is a natural companion to <|
On Mon, Jul 18, 2011 at 1:44 PM, Allen Wirfs-Brock wrote: >> Also, how about |> as opposed to <&, since it is a dual to <| adding >> own rather than inherited properties? > > I'd be a bit concerned about some people getting confused about which > direction of the "arrow" corresponds to each operation. I think the arrow-to-operator correspondence would be fairly easy to remember... "extension arrows point back in space to prototypes which exist back in time , and forward in space to own properties which will exist forward in time". > > I like the nemonic value of having + or & as part of the operator symbol for > "extend" but unfortunately <+ can't be used. One thing to note is that <& would disallow using & as a unary operator in the future. Here's another option to consider: // replace <| with <> let B = A <> {...}; // looks like a (prototype) chain link // then +> could make sense let b = B +> {}; // "adding" pointed to properties. Thanks, Sean Eagan ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: An "extend" operator is a natural companion to <|
On Jul 18, 2011, at 1:39 PM, Sean Eagan wrote: > On Mon, Jul 18, 2011 at 1:44 PM, Allen Wirfs-Brock > wrote: >>> Also, how about |> as opposed to <&, since it is a dual to <| adding >>> own rather than inherited properties? >> >> I'd be a bit concerned about some people getting confused about which >> direction of the "arrow" corresponds to each operation. > > I think the arrow-to-operator correspondence would be fairly easy to > remember... "extension arrows point back in space to prototypes which > exist back in time , and forward in space to own properties which will > exist forward in time". Whatever happens, we mix metaphors: 1. <| is pointing in the reference direction of __proto__ aka [[Prototype]]. 2. <& is pointing to the copy destination. However, making the arrow point to the right mixes yet another metaphor: 3. +> or whatever points to the copy sources. I do not think 3 helps as much as it hurts. Separate point: left to right assignment or copy operation is confusing in many languages, and right to left is the norm. This goes back to assembly variants (gdb x86 vs. Intel ASM). JS uses right to left assignment direction. I think this must matter. >> I like the nemonic value of having + or & as part of the operator symbol for >> "extend" but unfortunately <+ can't be used. > > One thing to note is that <& would disallow using & as a unary > operator in the future. Yup. No plans there. > Here's another option to consider: > > // replace <| with <> > let B = A <> {...}; // looks like a (prototype) chain link How so? That link is unidirectional. I don't buy it, and <> historically means not equal or unordered. > // then +> could make sense > let b = B +> {}; // "adding" pointed to properties. See above. /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Array.prototype.concat result length (ES5.1)
> On 07/14/2011 10:04 AM, Allen Wirfs-Brock wrote: >> It is probably a bug, because array index based operations generally warp >> around to 0 at 2^32. On 18 July 2011 19:51, Jeff Walden wrote: > Removing all the RangeError stuff, and making array indexes just > non-negative integers, would be nice for ES6 or similar. I suspect > changing that won't break anyone worth caring about, although I do know some > people have taken the time to care about this in the past (mostly in a > spec-nut way :-) ): > > http://hexmen.com/blog/2006/12/push-and-pop/ Hmm. That link has the following to say: - Steps 3 – 6 of push are a little ambiguous, and the specification probably should have stated that repeated increments to n must be done using 32-bit unsigned integer arithmetic – it’s kind-of implicit as n is assigned the result of the internal ToUint32 operator. Using 32-bit arithmetic leads to some strange edge-cases: when n overflows from 232-1 to 0, push will have set a property called 4294967295. This is strange, as 429496795 is not an array index (as discussed above), but at least it means the property-value will still be available after the inevitable array-truncation (when length is set to some small value in step 8.) That is actually contrary to my reading of ECMA-262 3ed. intentions. I assume 5 ed. has the same intentions, but I have not checked it. The way I read those intentions is as you can see in the error I reported in my comment to: http://blogs.msdn.com/b/jscript/archive/2008/03/25/performance-optimization-of-arrays-part-i.aspx (Opera fixed a different but related bug in the same algorithms in Futhark to follow the correct handling (as I read the spec). Carakan today I don't know about.) For those not wanting to go searching for my comment and sifting through that text: The algorithm in question uses ToUInt32 to convert the value, but the storage is not as uint32 but as pretty much everywhere in ECMAScript, Number, thus follows normal double arithmetics. This is particularly of note as it means that it should not wrap around - the purpose of the following parts of 15.4.5.1 [[Put]] (P, V): 12. Compute ToUint32(V). 13. If Result(12) is not equal to ToNumber(V), throw a RangeError exception. 14. For every integer k that is less than the value of the length property of A but not less than Result(12), if A itself has a property (not an inherited property) named ToString(k), then delete that property. 15. Set the value of property P of A to Result(12). is to, at step 13, catch the specific event of trying to exceed uint32 size with the length property and throw an error *instead of* wrapping around and as a result of step 14 of above algorithm destroy properties on the array. Wrapping around is an error, because ToUInt32 gives not a uint32 but a Number which can fit into a unit32 as result. IIRC the 3ed. spec never uses any other number format than Number, it only performs the operations to fit an input Number type into the limitations of those other number types, into an output of Number type. -- David "liorean" Andersson ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: An "extend" operator is a natural companion to <|
On Jul 18, 2011, at 11:26 AM, Dmitry A. Soshnikov wrote: > On 18.07.2011 21:32, Allen Wirfs-Brock wrote: >> >> I've recently been experimenting with coding both prototypal and class based >> object definitions using the various syntactic forms that are currently on >> the table. Something has emerged from that which has surprised me. I have >> never been a big fan of the "extend" method that is provided by a number of >> JavaScript frameworks. However, based upon my experiments I'm now think that >> something like extend solves several issues with the declarative object and >> class declarations that we have recently been discussing. >> >> Just in case there is anyone on this list who isn't familiar with extend, >> here is a quick explanation. In most frameworks that support it, "extend" is >> invoked something like this: >>obj.extend(extensionObj) >> It copies the own properties of extensionObj and makes corresponding own >> properties for obj. > > JFTR: object.extend(...) first appeared in Prototype.js and was borrowed > there from Ruby. In Ruby though, this method method is delegation-based (that > is, a hidden class is created which is inserted as a direct ancestor of the > object and the superclass of the hidden class is set to extending module). > That is, if something changes in the mixed module, the changes are reflected > on the object which extended it. > ... > Perhaps ES wants also this approach. OTOH, "extend" practice is already > strongly used in JS and from this viewpoint it makes sense to make extending > with copying and not using delegation. That would require at mutable [[Prototype]] which is something we have been trying to stay away from in TC39. > > ... > Am I alone here who want to put a space after the methods in this proposal? > ;) It's like operators, really: feel free to use one. a space isn't syntactically significant in that position. ... > > >> if (!this.__validate(x,y)) throw "invalid"; >> return this <| { >> __x: x, >> __y: y >> } >> }; >> } >> > > What's the reason we want back to desugared factories (with all this explicit > stuff, manual `return`, setting manually proto, etc) instead of improving > constructors syntax by forming them to classes? Is the syntax already > accepted for classes by the way? I remind again this syntactic form which > seems (for me?) more elegant > http://dmitrysoshnikov.com/scheme-on-coffee/class.html. One of the design issues in the class proposals has been how to distinguish between prototype properties and per instance properties added by a constructor. The current class proposal uses public/private declarations in the constructor for that purpose. This proposal of mine is suggesting that instead of adding special syntax just for constructors in class declarations we should consider adding a new operator (<&) with multiple uses that can also address the constructor use case. ... > I'm really sorry, perhaps it's only for me, but all these examples seem to me > too cryptic in respect of syntax (even like Perl). All these `} <& {` seem > for me as a syntactic noise. I even better would like to see some longer but > humanized "extends" keyword. Maybe it's about the habit and perhaps later it > will become for me not so cryptic (after all I can accept -> #, {||}, etc. as > an alternative for `function`). > I'm sympathetic to the crypticness argument but I get just as many arguments on the other side of the issue when proposing keyword based extensions. Also, it is generally harder (but not necessarily impossible) to add keyword operators then it is special character based operators. Automatic semi-colon insertion gets in the way. Consider: var function extend() {}; var bar = foo extend ({y:20}); > E.g. > > let foo = {x: 10} > > // delegation-based extending > let bar = foo extend { > y: 20; > }; > > // copy-based extending > bar.mixin({ > z: 30 > }); > > Pity btw, that < is already borrowed and is a normal operator, we could use > it instead of <| which is not the best on different fonts. value="Mayber a back arrow? <- " /> foo <-bar // foo < (-bar) Allen___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: An "extend" operator is a natural companion to <|
On Jul 18, 2011, at 1:05 PM, Bob Nystrom wrote: > On Mon, Jul 18, 2011 at 10:32 AM, Allen Wirfs-Brock > wrote: > > This is a nice declarative way to describe the per instance state but it > > turns out it doesn't generalize very well to multiple levels of inheritance. > > This is an important point. I think the reason most OOP languages make a > distinction between object construction and initialization is because in the > presence of inheritance, you need 1 construction, but N initializations where > N is the depth of your inheritance chain. If you try to construct and > initialize in one step, it's hard to do that (as your proposal addresses). > > > A similar issue exists for the proposed class declarations. That proposal > > includes the concept of "static" (a lot of us don't like that the term > > "static" in this context) property declaration: > > Yeah, I don't think anyone is crazy about that keyword, but it's: > > 1. Reserved already. But we are using it in a context where that isn't an issue. > 2. Familiar from other languages and it works about the same here as it does > in those Arguably it doesn't. Java/C# static methods are not inherited/over-ridable...of course that leads to the issue of constructor-side inheritance. > > I liked "class" instead, but the worry about nested classes was enough to > talk me out of it. > > We have a challenge with JS keywords: > > 1. We want to re-use keywords from other languages to make JS familiar and to > use what's already in the programmer's head. If some JS construct looks and > acts similar to a construct in other popular languages, using the same word > for it is like instant knowledge. I don't think we have listed this as a design guideline anywhere. If a word/constructor looks the same or even similar but has different semantics we have instance confusion rather than instant knowledge. We don't have to look familiar to get people to use JavaScript. They are going to use it regardless. We may be past the point where these is much value in mimicking other languages. > > 2. We want to emphasize JS's unique features. JS is not Java (or C++, or C#, > or Smalltalk, or Ruby) and we run the risk of leading users down a false path > if we make JS look superficially too much like them. There's also a strong > cultural bias where JS = light, terse, expressive, fun and Java = verbose, > rigid, work. Borrowing keywords from Java makes people worry that there's > going to be a JavaScript khaki dress code. Personally, I think that's much > ado about nothing, but I can understand why people would worry that the suits > are going to show up and crash the party. > > > Static properties are really just own properties of the constructor object. > > While sometimes useful, they occur relatively infrequently yet they > > require a additional declaration form within class bodies. > > I just did some quick hunting through a few classes in the Closure library. > Instance methods are definitely the most common kind of member there, but > static methods were used in every type I looked at. For example, > goog.ui.Component has a static mutable field, three "constants" which are > properties on the constructor, and a couple of static methods. > goog.ui.Tooltip has five members that would use "static" in the class > proposal. I was speaking primarily from Smalltalk experience. Even though it had very good tool support for class-side methods, there use is relatively rare. Certainly less than 5% of all methods particularly if you discount the class-side methods that implement and support the new method which is basically the Smalltalk equivalent of the normal constructor. They certainly shouldn't be particularly inconvenient to define. But they aren't a central feature of OO design and probably don't deserve quite as much language design attention as instance methods. BTW, you may find some of the metric at http://www.emunix.emich.edu/~ahmad/metrdes.htm interesting. > > > This complicates the conceptual model of a class declaration by allowing > > intermingling of constructor and prototype property declaration. > > There can also be confusion between what goes on the prototype and what goes > on the new instance. Exactly. I'm arguing that placing instance properties in an extension object literal within the constructor is a way to reduce this confusion without adding new property declarations that can only occur in the function bodies of constructor functions. > > > This also increase the potential for confusion about the meaning of "this" > > (and "super") within such static property declarations. > > Good point. From a conceptual simplicity argument, I like the idea of having > different curly blocks for "stuff on the ctor" and "stuff on the proto". > Pragmatically, though, C++, Java, and C# all mix instance and non-instance > members together and people seem to get by without too much troub
Re: An "extend" operator is a natural companion to <|
On Jul 18, 2011, at 1:37 PM, Brendan Eich wrote: > On Jul 18, 2011, at 12:16 PM, Allen Wirfs-Brock wrote: > >> On Jul 18, 2011, at 11:32 AM, Brendan Eich wrote: >> >>> Hawt. >>> >>> A bit rough in that LHS <& RHS mutates LHS, whereas LHS <| RHS is pure and >>> produces a new object (which could be optimized to mutate RHS, note well!). >>> Both <| and <& are operators, to support chaining. Would it be better for >>> <& to be pure as <| is, and make an assignment operator form, LHS <&= RHS, >>> that expands to LHS = LHS <& RHS? >> >> One way to think about <& is as a more declarative and more concise >> alternative to Object.defineProperties. I think there are plenty of use >> causes for the mutating extends. For example, adding properties to the >> prototype object of a function. Another issue is that the object you need >> to extend may already have been captured somewhere. For example, a >> super.constructor call might have registered the object in a WeakMap and if >> <& created a new instance you would loose the identify relationship. > > Sure, I'm not saying word one against the mutating form -- just noting the > symmetry break w.r.t. <| and suggesting a fix that builds on the assignment > operator. You're right it can be viewed that way. Even though I know that = or += and friends are just operators I think I tend to think of them more like a statement form. Also all the other assignment operators are operations upon values and they modify a variable rather than mutate an object. I see where you're coming from on this but personally that lack of symmetry with the other assignment operators feels more disconcerting then the lack of symmetry with <|. > > >> In practice, I think most of the more declarative uses such as adding own >> properties can be implemented (and perhaps even specified) such that no >> extra objects need to be created. > > Agreed. > > >> One thing that I think is still open is whether the RHS needs to be an >> ObjectLiteral or whether any object producing expression should be allowed. >> All the use cases I have explored use an ObjectLiteral but I don't see any >> hazard (like would be the case if <| mutated the LHS [[Prototype]]) with >> allowing a non-literal value of the RHS. > > Probably right; another symmetry break. Yes, if the symmetry argument would be my primary reason for restricting the RHS. But then <& could be use for things like: Object.prototype.extend = function (obj) {this <& obj}; Allen ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: An "extend" operator is a natural companion to <|
On Mon, Jul 18, 2011 at 3:40 PM, Allen Wirfs-Brock wrote: > > On Jul 18, 2011, at 1:05 PM, Bob Nystrom wrote: > > 2. Familiar from other languages and it works about the same here as it > does in those > > Arguably it doesn't. Java/C# static methods are not > inherited/over-ridable...of course that leads to the issue of > constructor-side inheritance. > I did say "*about* the same" and not *exactly* the same. Naturally there are some differences, but there are plenty of similarities too. I don't think a programmer from C++/C#/Java would find this astonishing at all, and would probably have little trouble inferring what it means: class Point { static zero() { return new Point(0, 0); } } var p = Point.zero(); 1. We want to re-use keywords from other languages to make JS familiar and > to use what's already in the programmer's head. If some JS construct looks > and acts similar to a construct in other popular languages, using the same > word for it is like instant knowledge. > > I don't think we have listed this as a design guideline anywhere. > Does it need to be written down as a guideline to be a good idea? > If a word/constructor looks the same or even similar but has different > semantics we have instance confusion rather than instant knowledge. > > We don't have to look familiar to get people to use JavaScript. They are > going to use it regardless. > That's probably true (yay browser lock-in) but I don't know that's what I'd call a great attitude for usability. I'm imagining that as a bumper sticker. JavaScript: you're going to use it regardless. We may be past the point where these is much value in mimicking other > languages. > What makes you say that? > I was speaking primarily from Smalltalk experience. Even though it had > very good tool support for class-side methods, there use is relatively > rare. > I'm not a Smalltalker but my impression is that Smalltalk culture was more open to defining things as instance methods where a Javascripter would likely make a static method on some constructor. Especially in recent years where monkey-patching existing protos is considered bad form, that leads to fewer instance methods and more static ones. Likewise, for-in loops discourage adding methods on prototypes. Even in Harmony, many of the new methods being adding are "static": Proxy.create(), Proxy.createFunction(), Proxy.isTrapping(), Object.getPropertyDescriptor(), Object.getPropertyNames(), Object.is(), Number.isFinite(), Number.isNan()... If anything non-instance methods are becoming more important in JS as time goes on (though modules might change that). Certainly less than 5% of all methods particularly if you discount the > class-side methods that implement and support the new method which is > basically the Smalltalk equivalent of the normal constructor. > My rough calculation was around 10% looking at Closure JS code. > They certainly shouldn't be particularly inconvenient to define. But they > aren't a central feature of OO design and probably don't deserve quite as > much language design attention as instance methods. > Agreed. > The primary purpose of a class is to define the behavior (methods) of > instances. It is describing an abstraction over all the possible instances. > The behavior of the singleton class object is typically secondary to the > primary abstraction. > True, but secondary still matters. > 3. Unless I've had it explained to me, there's no way I could figure out > what that code means even if I'm an expert in other programming languages. > > Yes,but how long will it take you to learn it. Isn't it better when > learning something new to know that you don't understand it rather than to > being mislead into thinking it is something familiar when it really isn't. > I don't like being led astray, certainly. Personally, I do like learning "this is kinda like a foo" and then later having that refined to "but it doesn't bar and it bazzes". I'd rather that than having to learn a new concept from a blank page only to reach the end and realize "hey, this is 75% like a foo." - bob ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: An "extend" operator is a natural companion to <|
On Jul 18, 2011, at 4:50 PM, Bob Nystrom wrote: > On Mon, Jul 18, 2011 at 3:40 PM, Allen Wirfs-Brock > wrote: > > On Jul 18, 2011, at 1:05 PM, Bob Nystrom wrote: >> 2. Familiar from other languages and it works about the same here as it does >> in those > Arguably it doesn't. Java/C# static methods are not > inherited/over-ridable...of course that leads to the issue of > constructor-side inheritance. > > I did say "about the same" and not exactly the same. Naturally there are some > differences, but there are plenty of similarities too. I don't think a > programmer from C++/C#/Java would find this astonishing at all, and would > probably have little trouble inferring what it means: > > class Point { > static zero() { return new Point(0, 0); } > } > > var p = Point.zero(); Indeed, isn't that legal C# 4.0? :-P > >> 1. We want to re-use keywords from other languages to make JS familiar and >> to use what's already in the programmer's head. If some JS construct looks >> and acts similar to a construct in other popular languages, using the same >> word for it is like instant knowledge. > > I don't think we have listed this as a design guideline anywhere. > > Does it need to be written down as a guideline to be a good idea? Zing! I've been on this side since ES4 days. I'm a bit older and wiser, so I'll just add that it takes nice judgment and experience to know when to imitate and when to diverge. It's clear classes are not quite complete or coherent for ES.next, even though the proposal has been accepted (too many open issues, some not recorded). Imitating another language won't settle the open issues. We need to focus on specifics and simplify if possible, cut if necessary. > > If a word/constructor looks the same or even similar but has different > semantics we have instance confusion rather than instant knowledge. > > We don't have to look familiar to get people to use JavaScript. They are > going to use it regardless. > > That's probably true (yay browser lock-in) but I don't know that's what I'd > call a great attitude for usability. I'm imagining that as a bumper sticker. > JavaScript: you're going to use it regardless. I'm with Bob here. Word on the street, from folks ranging the skill gamut, is that <|, <& and so on are Perl-ish line noise. We should consider alternatives, even if it means restricted productions. > The primary purpose of a class is to define the behavior (methods) of > instances. It is describing an abstraction over all the possible instances. > The behavior of the singleton class object is typically secondary to the > primary abstraction. > > True, but secondary still matters. Here you were arguing about class vs. prototype method indentation or body nesting level? I could see us making a subordinate body for class methods, instead of using 'static'. /be___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: An "extend" operator is a natural companion to <|
On Mon, Jul 18, 2011 at 4:59 PM, Brendan Eich wrote: > The primary purpose of a class is to define the behavior (methods) of >> instances. It is describing an abstraction over all the possible instances. >> The behavior of the singleton class object is typically secondary to the >> primary abstraction. >> > > True, but secondary still matters. > > Here you were arguing about class vs. prototype method indentation or body > nesting level? > I think Allen was explaining why it makes sense for "static" methods to be in a second curly block following the main class definition. I could see us making a subordinate body for class methods, instead of using > 'static'. Interesting idea! Something like this? class Point { constructor(x, y) { this.x = x; this.y = y; } manhattanDistance() { return Math.abs(this.x) + Math.abs(this.y); } ??? { zero() { return new Point(0, 0); } unit() { return new Point(1, 1); } } } It looks a little strange to me to lump the class members together like that and leave the prototype ones directly under class given that the prototype members don't end up on the class. You could flip it around to: class Point { constructor(x, y) { this.x = x; this.y = y; } zero() { return new Point(0, 0); } unit() { return new Point(1, 1); } prototype { manhattanDistance() { return Math.abs(this.x) + Math.abs(this.y); } } } Pros: 1. Makes it abundantly clear that prototype members are just that. 2. Makes it clearer (I think?) that members on the constructor ("class") are that. Basically, the name of the surrounding curly ("prototype" or "class") tells you where the member goes. Cons: 1. Prototype members, the most common case, are the most verbose and suffer two levels of indentation. 2. constructor() is in weird limbo. Should it be under prototype or class? 3. Lots'o'curlies. There may be a good solution hiding in here somewhere, but I'm not sure. - bob ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: An "extend" operator is a natural companion to <|
On Jul 18, 2011, at 5:31 PM, Bob Nystrom wrote: > class Point { > constructor(x, y) { > this.x = x; > this.y = y; > } > > zero() { > return new Point(0, 0); > } > > unit() { > return new Point(1, 1); > } > > prototype { > manhattanDistance() { > return Math.abs(this.x) + Math.abs(this.y); > } > } > } > > Pros: > 1. Makes it abundantly clear that prototype members are just that. But that's not a good in itself, and your argument about many other languages putting "instance methods" at the level that this approach uses for class methods goes against. > 2. Makes it clearer (I think?) that members on the constructor ("class") are > that. Basically, the name of the surrounding curly ("prototype" or "class") > tells you where the member goes. This is a fair point but I think it is outweighed by the Cons (mine plus yours). > Cons: > 1. Prototype members, the most common case, are the most verbose and suffer > two levels of indentation. That's a problem too (in addition to the strangeness compared to other languages with 'class' syntax). > 2. constructor() is in weird limbo. Should it be under prototype or class? It binds a prototype property (MyDate.prototype.constructor), absent more magic to make that alias. It does not bind a class property (e.g., MyDate.constructor). > 3. Lots'o'curlies. This is a pain, but so are lots o' statics -- but generally class methods are few, so it could be a wash. > There may be a good solution hiding in here somewhere, but I'm not sure. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: An "extend" operator is a natural companion to <|
On Jul 18, 2011, at 4:50 PM, Bob Nystrom wrote: > > > On Mon, Jul 18, 2011 at 3:40 PM, Allen Wirfs-Brock > wrote: > > On Jul 18, 2011, at 1:05 PM, Bob Nystrom wrote: >> 2. Familiar from other languages and it works about the same here as it does >> in those > Arguably it doesn't. Java/C# static methods are not > inherited/over-ridable...of course that leads to the issue of > constructor-side inheritance. > > I did say "about the same" and not exactly the same. Naturally there are some > differences, but there are plenty of similarities too. I don't think a > programmer from C++/C#/Java would find this astonishing at all, and would > probably have little trouble inferring what it means: But if you were coming from a language where constructors (classes) were real objects with real methods that could reference this and which were inherited by subclass object you might look at the issue quite differently > > class Point { > static zero() { return new Point(0, 0); } > } in fact you might look at the above zero method and say, Oh, that's buggy. He didn't think about what will happen when somebody creates Point3D as a subclass and then codes: Point3D.zero(); A better definition would be: class Point { static zero() { return new this(0, 0); } } or, probably even better this: class Point { static zero() { return new this( ); } } and you probably would want to make sure that the constructor always return the origin for the default no argument case. The main point is that to do the right think here it is important to not think of it as being just like C++/C#/Java > > var p = Point.zero(); > >> 1. We want to re-use keywords from other languages to make JS familiar and >> to use what's already in the programmer's head. If some JS construct looks >> and acts similar to a construct in other popular languages, using the same >> word for it is like instant knowledge. > > I don't think we have listed this as a design guideline anywhere. > > Does it need to be written down as a guideline to be a good idea? The real issue is what is both looks and acts similarly enough to make this a good rule to apply. There is clearly some disagreement about this particular case and a hazard of this general rule. > > If a word/constructor looks the same or even similar but has different > semantics we have instance confusion rather than instant knowledge. > > We don't have to look familiar to get people to use JavaScript. They are > going to use it regardless. > > That's probably true (yay browser lock-in) but I don't know that's what I'd > call a great attitude for usability. I'm imagining that as a bumper sticker. > JavaScript: you're going to use it regardless. I wasn't trying to make a statement about usability. I actually think usability (and readability) of language features is very important. But I'm more concerned about the long term usability of the language by people who know the language well then I am about short term ease of adoption by somebody today who is knowledgeable about some in a legacy language. JavaScript is here and and going to remain here for a very long time. I want it to hold together on its own terms and to be most usable for people for whom it is their first and primary programming language. > > We may be past the point where these is much value in mimicking other > languages. > > What makes you say that? JavaScript is its own unique combination of concepts and features. I think that some of the more significant warts in JavaScript (including the new operator, instanceof, and some aspects of constructor functions) are a direct result of such mimicry. Rather than continuing to just graft on features from other language we need to be evolving JavaScript on its own terms. This needs to be informed by knowledge of the successes and failures of other language designs and many other things. But I see little value in mimicry for its own sake. > > I was speaking primarily from Smalltalk experience. Even though it had very > good tool support for class-side methods, there use is relatively rare. > > I'm not a Smalltalker but my impression is that Smalltalk culture was more > open to defining things as instance methods where a Javascripter would likely > make a static method on some constructor. Especially in recent years where > monkey-patching existing protos is considered bad form, that leads to fewer > instance methods and more static ones. Likewise, for-in loops discourage > adding methods on prototypes. This may well be the case, but is it something we want to encourage for the next 50 years? Most behavior belongs on instances because it is the instance that model the computation system of the program. Instances are what OO design and programming is all about. If we make it easy to define instance behavior and harder to define "static" behavior that is probably a good think in the long run.
Re: An "extend" operator is a natural companion to <|
On Jul 18, 2011, at 4:59 PM, Brendan Eich wrote: > > Word on the street, from folks ranging the skill gamut, is that <|, <& and so > on are Perl-ish line noise. We should consider alternatives, even if it means > restricted productions. As I've said in the past, I'm generally more in the COBOL/readability camp than I am in the APL/terseness camp (in reality, I more of a PL/I guy. (and what's this new fangled Perl thing that everybody keeps talking about??)) That said, in writing sample code I've come to find <| to be rather pleasant to both write and read. Beyond that, we need to really decide what we want to surface syntax of JS to be like as it evolve. Do we want a keyword rich language or a concise language that uses lots of special characters. How to we find the balance between the extreme. For now I don't think we really have anything to guide us so we keep oscillate from on to the other based upon the latest feedback on a proposal that goes one way or the other. Allen ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
An "extend" operator is a natural companion to <|
> From: Allen Wirfs-Brock > Date: July 18, 2011 19:32:24 GMT+02:00 > To: es-discuss > Subject: An "extend" operator is a natural companion to <| Definitely a nice dual to <| > proto <| obj What happens if obj is not a literal? Then it would make sense to do a shallow copy of obj whose prototype is proto. That would be useful for combining objects into a chain. From your examples, it looks as if the lhs would be modified, a bit similar to the += operator. Then the "arrow" should probably point in the opposite direction, e.g.: > objToBeModified +> increment Quoting from your examples: > function Point(x,y) { >return tthis <& { > __x: x, > __y: y > }; > }; > Point.prototype <& { >__validate(x,y) { return typeof x == 'number' && typeof y = 'number'} > }; I love how the prototype is incremented here. What does "tthis" do? Wouldn't point simply return an object literal (no "this <&")? Another example from your message: > 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 > } > }; > } If things are ever done this way, I would prefer to have an initialize() method (that only initializes an instance that has been created for it) and not a method that instantiates and initializes at the same time. initialize() works together very well with super-references, because in subclasses, you simply call the super-initialize method. -- 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: An "extend" operator is a natural companion to <|
> From: Bob Nystrom > Date: July 19, 2011 2:31:48 GMT+02:00 > To: Brendan Eich > Cc: es-discuss > Subject: Re: An "extend" operator is a natural companion to <| To me, the important thing with class literals is to use naming consistently. If the class is named Point and you want to add properties to it, it seems odd to use an operator to add those properties to (what looks like) a method called "constructor". Under the hood, it makes sense. Superficially (with a sugared eye), it’s confusing. > You could flip it around to: > > class Point { > constructor(x, y) { > this.x = x; > this.y = y; > } > > zero() { > return new Point(0, 0); > } > > unit() { > return new Point(1, 1); > } > > prototype { > manhattanDistance() { > return Math.abs(this.x) + Math.abs(this.y); > } > } > } I kind of like the idea of parameterized object literals. But then I have no idea where class properties would go: class Point(x,y) { x: x, y: y, prototype { manhattanDistance() { return Math.abs(this.x) + Math.abs(this.y); } } } -- 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: An "extend" operator is a natural companion to <|
On Jul 18, 2011, at 6:50 PM, Allen Wirfs-Brock wrote: >> Even in Harmony, many of the new methods being adding are "static": >> Proxy.create(), Proxy.createFunction(), Proxy.isTrapping(), >> Object.getPropertyDescriptor(), Object.getPropertyNames(), Object.is(), >> Number.isFinite(), Number.isNan()... If anything non-instance methods are >> becoming more important in JS as time goes on (though modules might change >> that). > > Brendan and I recently had a discussion about some of these and it isn't > clear that they are all in the correct places. Of the ones you mentioned, > I'm not so sure I would call Proxy a constructor. (you never say, new > Proxy() right?) it is really a factory object which means the methods you > name are instance side. No, rather Proxy is a module name. We could even spec it for ES.next as something users must import from a standard library MRL: module Proxy from "@proxy"; and avoid polluting the global scope with 'Proxy' -- users get to name the module. With ES.next modules, one can even selectively import: import createFunction from "@proxy"; > The Object refection methods were put on object because it was a convenient > "namespace" object. But that's not a style I would want to encourage for > complex user applications. It depends on whether we have set a precedent, and also on the domain type (see below). > isFinite and isNaN probably should go on the instance side. No, that requires an extra test, as we discussed re: isGenerator. These are intentional any -> boolean non-coercing functions, not methods. They must be callable on any type of argument without testing that the argument is a number and then using it to call a method. /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: An "extend" operator is a natural companion to <|
On Jul 18, 2011, at 7:00 PM, Allen Wirfs-Brock wrote: > On Jul 18, 2011, at 4:59 PM, Brendan Eich wrote: > >> >> Word on the street, from folks ranging the skill gamut, is that <|, <& and >> so on are Perl-ish line noise. We should consider alternatives, even if it >> means restricted productions. > > As I've said in the past, I'm generally more in the COBOL/readability camp > than I am in the APL/terseness camp (in reality, I more of a PL/I guy. (and > what's this new fangled Perl thing that everybody keeps talking about??)) > > That said, in writing sample code I've come to find <| to be rather pleasant > to both write and read. I can believe that. We should in any case not rush to judgment. "consider alternatives" means writing some side-by-side examples, playing with alternatives in practical code. > Beyond that, we need to really decide what we want to surface syntax of JS to > be like as it evolve. Do we want a keyword rich language or a concise > language that uses lots of special characters. How to we find the balance > between the extreme. For now I don't think we really have anything to guide > us so we keep oscillate from on to the other based upon the latest feedback > on a proposal that goes one way or the other. The main feedback is "don't grow the surface syntax too much". ES4 did overreach here, based on original-JS2/ES4 precedent and AS3. We should in any case not add "too much" (to be defined). /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: An "extend" operator is a natural companion to <|
Hey Bob, FWIW... > class Point { > constructor(x, y) { > this.x = x; > this.y = y; > } > > zero() { > return new Point(0, 0); > } > > unit() { > return new Point(1, 1); > } > > prototype { > manhattanDistance() { > return Math.abs(this.x) + Math.abs(this.y); > } > } > } > ...That's actually the nicest, most intuitively designed "class" structure I've seen so far. Rick ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: An "extend" operator is a natural companion to <|
On Jul 18, 2011, at 9:02 PM, Rick Waldron wrote: > Hey Bob, FWIW... > > > class Point { > constructor(x, y) { > this.x = x; > this.y = y; > } > > zero() { > return new Point(0, 0); > } > > unit() { > return new Point(1, 1); > } > > prototype { > manhattanDistance() { > return Math.abs(this.x) + Math.abs(this.y); > } > } > } > > ...That's actually the nicest, most intuitively designed "class" structure > I've seen so far. This is actually close to a less magical syntax that flips around the "ClassElements" (immediate children of the class {...} container) to specify class methods. Here's a less sugared version: class Point { zero() { return new Point(0, 0); } unit() { return new Point(1, 1); } prototype: { constructor(x, y) { this.x = x; this.y = y; } manhattanDistance() { return Math.abs(this.x) + Math.abs(this.y); } } } Notice how constructor is in the prototype object, as in JS with constructors and prototypes. Also, prototype: {...} not prototype {...}. As Bob noted, though, even in JS and moreso (from Allen's stats) in Smalltalk, class methods are uncommon compared to prototype methods. Do you really want an extra level of indentation for the latter? This example is skewed away from norms by having two class methods to one prototype method. I think constructor was mislocated, and when I fixed it just above, the instance- vs. class-method counts matched. Still unrealistic, though. If we want *more* magic from the class syntax, not less, we could use 'new' at the outer class-element indentation level to declare the constructor, and automagically impute 'constructor' in the prototype from it. But we'd still be over-indenting the prototype methods, which are many, compared to the class methods (few). What was that Spock said about the needs of the many? /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss