Syntax Proposal: Allow Java-like Object Literals after constructor calls to set properties on created objects.
A lot of people put opening semicolons on a new line, including the Rhino authors. How would semicolon insertion in this proposal interact with that formatting convention? var runnable = new java.lang.Runnable() { run: function () { } };
2010/6/8 Jürg Lehni <lists at scratchdisk.com>:
I am still interested in hearing more feedback on this. Maybe my examples were not so clear?
As more real world example, taken from a UI library that I am working with, would look like this:
var stopButton = new ImageButton(this) {
image: getImage('stop.png'),
size: buttonSize,
toolTip: 'Stop Running Scripts',
onClick: stopAll
};
Again, all the properties from the object literal immediately following the constructor call would then be set on the created object.
Rhino allows me to use this already and it has been proven to be very useful in many occasions, leading to cleaner and more readable code.
Jürg
Just out of curiosity, what's wrong with the idiomatic Javascript way of passing an object literal as your last constructor argument? So your example becomes:
var stopButton = new ImageButton(this, {
image: getImage('stop.png'),
size: buttonSize,
toolTip: 'Stop Running Scripts',
onClick: stopAll
});
Granted, you wind up with an extra comma...
2010/6/30 Jürg Lehni <lists at scratchdisk.com>:
I am still interested in hearing more feedback on this. Maybe my examples were not so clear?
As more real world example, taken from a UI library that I am working with, would look like this:
var stopButton = new ImageButton(this) { image: getImage('stop.png'), size: buttonSize, toolTip: 'Stop Running Scripts', onClick: stopAll };
Would this be semantically equivalent to var stopButton = new ImageButton(this, { image: ... }); ?
So you are moving the object literal inside the parentheses, instead of automatically mixing properties into the instance. So it is not the same as var tmp = <instance of ImageButton before ctor called> tmp.image = ...; tmp.size = ...; ... ImageButton.call(tmp, this); ?
Again, all the properties from the object literal immediately following the constructor call would then be set on the created object.
It's the "immediately following bit" that I was asking about earlier. It seems to interact badly with semicolon insertion.
For example, new ImageButton(this) { image: getImage('stop.png') };
Rhino allows me to use this already and it has been proven to be very useful in many occasions, leading to cleaner and more readable code.
Because of the way you place curly brackets at the end of a line.
On Jun 30, 2010, at 10:05 AM, Jürg Lehni wrote:
I am still interested in hearing more feedback on this. Maybe my examples were not so clear?
As more real world example, taken from a UI library that I am working with, would look like this:
var stopButton = new ImageButton(this) { image: getImage('stop.png'), size: buttonSize, toolTip: 'Stop Running Scripts', onClick: stopAll };
Again, all the properties from the object literal immediately following the constructor call would then be set on the created object.
Rhino allows me to use this already and it has been proven to be very useful in many occasions, leading to cleaner and more readable code.
Some of us old-timers were around at Netscape with Norris Boyd when this was designed and added to Rhino. It was not added to SpiderMonkey, though, for not terribly compelling reasons.
I remember being concerned about the ASI issue, which requries a [no LineTerminator here] restricted production. That is still a concern: the TC39 committee doesn't want to add restricted productions without very good reason.
This came up later, via bugzilla.mozilla.org/show_bug.cgi?id=253138 -- see bugzilla.mozilla.org/show_bug.cgi?id=253138#c7. bugzilla.mozilla.org/show_bug.cgi?id=253138#c10 suggests an operator of some sort to "merge" the objects.
Another point to note is that [1,2,3] is not equivalent to new Array(3) {0:1, 1:2, 2:3}, because the array initialiser form users the original value of Array.prototype, and it does not call the current binding of Array as a constructor.
So, more work needed to avoid a restricted production, at least. A linking operator or keyword ("with" a la functional record update) would help.
On Jun 30, 2010, at 11:13 AM, Brendan Eich wrote:
So, more work needed to avoid a restricted production, at least. A linking operator or keyword ("with" a la functional record update) would help.
(of course I was kidding about "with" :-P)
Not quite as concise but:
var stopButton = Object.create(ImageButton.prototype, { image: {value: getImage('stop.png')}, size: {value: buttonSize}, toolTip: {value: 'Stop Running Scripts'}, onClick: {value:stopAll} });
Of course, if you wanted something other than the default attribute values you would have to also add those.
Sorry, Object.create was a mistake. It is only useful for low level code and not for every day usage. Also, to use Object.create you need the following which is even worse for an every day usage.
var stopButton = Object.create(ImageButton.prototype, { image: { value: getImage('stop.png'), writable: true, configurable: true, enumerable: true }, size: { value: buttonSize, writable: true, configurable: true, enumerable: true }, toolTip: { value: 'Stop Running Scripts', writable: true, configurable: true, enumerable: true }, onClick: { value:stopAll, writable: true, configurable: true, enumerable: true } });
Even with all this boilerplate code it does not cover the code that happens inside the constructor so the code would get even longer.
var stopButton = new ImageButton(this); Object.defineProperties(stopButton, { image: { value: getImage('stop.png'), writable: true, configurable: true, enumerable: true }, size: { value: buttonSize, writable: true, configurable: true, enumerable: true }, toolTip: { value: 'Stop Running Scripts', writable: true, configurable: true, enumerable: true }, onClick: { value: stopAll, writable: true, configurable: true, enumerable: true } });
erik
On Jun 30, 2010, at 2:15 PM, Erik Arvidsson wrote:
Sorry, Object.create was a mistake.
A bit harsh, but my point is not about tone -- it is that the mistake in your view is the default values for missing attributes being false, not true. Right?
On 30 Jun 2010, at 18:26, Jeff Watkins wrote:
Just out of curiosity, what's wrong with the idiomatic Javascript way of passing an object literal as your last constructor argument? So your example becomes:
var stopButton = new ImageButton(this, { image: getImage('stop.png'), size: buttonSize, toolTip: 'Stop Running Scripts', onClick: stopAll });
Granted, you wind up with an extra comma...
The difference of my proposal is that this would work with any constructor: All the properties of the provided object literal would be mixed into the created object. Your proposal would require special constructors for each type that would accept an optional last argument containing an object with all these properties. Each of these constructors would then have to iterate over the properties and copy them over. I proposed to automate this, as from my experience it appears to be something that would often be of great use.
Just to clarify. These two examples would produce the same result:
var stopButton = new ImageButton(this) {
image: getImage('stop.png'),
size: buttonSize,
toolTip: 'Stop Running Scripts',
onClick: stopAll
};
VS: var stopButton = new ImageButton(this); stopButton.image = getImage('stop.png'); stopButton.size = buttonSize; stopButton.toolTip = 'Stop Running Scripts', stopButton.onClick = stopAll;
The ImageButton constructor would not provide any magic itself, and it would never actually see the object literal. This would work out of the box with any constructor.
In this particular example, ImageButton is actually a Java class that is used through Rhino's Java bridge feature. Setting image / size / toolTip properties then internally calls the setImage / setSize / setToolTip bean property setters. As far as I understand, the proposal by Allen Wirfs-Brock to use Object.create for this would there not work for two reasons: ImageButton.prototype is not available for such native java classes, and overriding properties through new property definitions would not cause the previous setters to be called.
Jürg
On 30 Jun 2010, at 23:32, Brendan Eich wrote:
On Jun 30, 2010, at 2:15 PM, Erik Arvidsson wrote:
Sorry, Object.create was a mistake.
A bit harsh, but my point is not about tone -- it is that the mistake in your view is the default values for missing attributes being false, not true. Right?
Rather than having missing boolean values default to true which somehow contradicts ES behavior when dealing with undefined boolean values (!undefined == true), I would have preferred if the properties were defined as their opposites, so default to false would make more sense: writable -> readOnly, configurable -> sealed, etc. But I guess it is what it is now.
Jürg
On Jun 30, 2010, at 4:24 PM, Jürg Lehni wrote:
On 30 Jun 2010, at 23:32, Brendan Eich wrote:
On Jun 30, 2010, at 2:15 PM, Erik Arvidsson wrote:
Sorry, Object.create was a mistake.
A bit harsh, but my point is not about tone -- it is that the mistake in your view is the default values for missing attributes being false, not true. Right?
Rather than having missing boolean values default to true which somehow contradicts ES behavior when dealing with undefined boolean values (!undefined == true), I would have preferred if the properties were defined as their opposites, so default to false would make more sense: writable -> readOnly, configurable -> sealed, etc. But I guess it is what it is now.
This was all discussed in the ES3.1 -> ES5 days, indeed. The ES1-3 "DontDelete", "DontEnum", and "ReadOnly" names are awkward for being negative ("Dont", "-Only"), resulting in double negative phrasing when documenting and discussing.
And you're right that attribute-property-missing -> undefined -> false has an effect here. If we had kept the ES3 negative names, we could have defaulted to false and Erik (I think) would not find Object.create a mistake -- but then the high-integrity-by-default fans would be put out. Those fans should speak up if they care to defend against the "mistake" charge.
Anyway, ES5 is done. Life goes on, though, with Harmony. There's not much that can be done about the default attribute values and the sense of their names, Same for Object.keys vs. Object.getOwnPropertyNames method-naming-style disparity. Food for thought, in order to do better in the future.
On 30 Jun 2010, at 19:13, Brendan Eich wrote:
Some of us old-timers were around at Netscape with Norris Boyd when this was designed and added to Rhino. It was not added to SpiderMonkey, though, for not terribly compelling reasons.
Do you remember what the reason was why this was added to Rhino in the first place? Was it to facilitate Java-like syntax when implementing interfaces? I remember seeing examples with such code quite a while ago, triggering me playing around with the feature and finding out that the object is simply passed as a last added argument. I did not manage to find any example code that somehow makes reference to this recently though, so I am wondering if the Rhino people are maybe trying to get rid of this too on the long term? After all it is not proper ES syntax, and for example Eclipse's syntax checker is complaining about it.
I remember being concerned about the ASI issue, which requries a [no LineTerminator here] restricted production. That is still a concern: the TC39 committee doesn't want to add restricted productions without very good reason.
This came up later, via bugzilla.mozilla.org/show_bug.cgi?id=253138 -- see bugzilla.mozilla.org/show_bug.cgi?id=253138#c7. bugzilla.mozilla.org/show_bug.cgi?id=253138#c10 suggests an operator of some sort to "merge" the objects.
Another point to note is that [1,2,3] is not equivalent to new Array(3) {0:1, 1:2, 2:3}, because the array initialiser form users the original value of Array.prototype, and it does not call the current binding of Array as a constructor.
So, more work needed to avoid a restricted production, at least. A linking operator or keyword ("with" a la functional record update) would help.
This is all very interesting. An operator would make the feature even more useful, as it would not be bound to the calling of a constructor. Could a simple : work? E.g.:
new Array(3) : { 0:1, 1:2, 2:3 };
Jürg
I actually think that our biggest mistake in designing ES5 was making the attribute defaults used by defineProperty different from the defaults use by other property creation methods. I think the new attribute names are fine but the argument that they needed to default to false is weaker than the desirability for consistancy across all property creation techniques. But what's done is done and past mistakes serve as good lessons for the future.
On Wed, Jun 30, 2010 at 4:31 PM, Brendan Eich <brendan at mozilla.com> wrote:
On Jun 30, 2010, at 4:24 PM, Jürg Lehni wrote:
On 30 Jun 2010, at 23:32, Brendan Eich wrote:
On Jun 30, 2010, at 2:15 PM, Erik Arvidsson wrote:
Sorry, Object.create was a mistake.
A bit harsh, but my point is not about tone -- it is that the mistake in your view is the default values for missing attributes being false, not true. Right?
Rather than having missing boolean values default to true which somehow contradicts ES behavior when dealing with undefined boolean values (!undefined == true), I would have preferred if the properties were defined as their opposites, so default to false would make more sense: writable -> readOnly, configurable -> sealed, etc. But I guess it is what it is now.
This was all discussed in the ES3.1 -> ES5 days, indeed. The ES1-3 "DontDelete", "DontEnum", and "ReadOnly" names are awkward for being negative ("Dont", "-Only"), resulting in double negative phrasing when documenting and discussing.
And you're right that attribute-property-missing -> undefined -> false has an effect here. If we had kept the ES3 negative names, we could have defaulted to false and Erik (I think) would not find Object.create a mistake -- but then the high-integrity-by-default fans would be put out. Those fans should speak up if they care to defend against the "mistake" charge.
Fine. Had it defaulted to low integrity, that would have been a mistake. Erik & I know we disagree on this.
Anyway, ES5 is done. Life goes on, though, with Harmony. There's not much that can be done about the default attribute values and the sense of their names, Same for Object.keys vs. Object.getOwnPropertyNames method-naming-style disparity. Food for thought, in order to do better in the future.
The naming inconsistency here actually is a mistake. I do regret it, but I also agree it's too late to fix it.
On Jun 30, 2010, at 7:37 PM, Mark S. Miller wrote:
And you're right that attribute-property-missing -> undefined -> false has an effect here. If we had kept the ES3 negative names, we could have defaulted to false and Erik (I think) would not find Object.create a mistake -- but then the high-integrity-by-default fans would be put out. Those fans should speak up if they care to defend against the "mistake" charge.
Fine. Had it defaulted to low integrity, that would have been a mistake. Erik & I know we disagree on this.
Allen seems to agree with Erik. Is this just a matter of personal opinion? The point that I don't see you responding to is that the Object.create defaults are opposite from what every other way of binding a property in the language uses.
Ok, that could be answered by arguing that Object.create needs different defaults for different use-cases from those other property-creating forms.
But then the current thread brought up a Rhino extension that does not use the high-integrity defaults. So here we are. What is the common case, in current best practices? Is it really high-integrity with opt-out?
On Jun 30, 2010, at 9:09 PM, Brendan Eich wrote:
On Jun 30, 2010, at 7:37 PM, Mark S. Miller wrote:
And you're right that attribute-property-missing -> undefined -> false has an effect here. If we had kept the ES3 negative names, we could have defaulted to false and Erik (I think) would not find Object.create a mistake -- but then the high-integrity-by-default fans would be put out. Those fans should speak up if they care to defend against the "mistake" charge.
Fine. Had it defaulted to low integrity, that would have been a mistake. Erik & I know we disagree on this.
Allen seems to agree with Erik. Is this just a matter of personal opinion? The point that I don't see you responding to is that the Object.create defaults are opposite from what every other way of binding a property in the language uses.
Ok, that could be answered by arguing that Object.create needs different defaults for different use-cases from those other property-creating forms.
I think the fact that you have to go out of your way to get normal defaults is a considerable inconvenience in many situations. I think Object.create would be easier to deploy if its defaults matched other ways of creating properties.
, Maciej
While I really do think we made a poor choice, from a usability perspective, I've also come to the conclusion that it's really not a big problem. Most of the time and particularly for methods people really don't need to reconfigure or enumerate properties so the "high-integrity" attributes will still be satisfactory even if they are seemingly arbitrarily different.
The strawman:obj_initialiser_meta proposal would allow an object literal to be used as an alternative to Object.create and would give you "normal" default attribute values.
Allen
From: es-discuss-bounces at mozilla.org [mailto:es-discuss-bounces at mozilla.org] On Behalf Of Maciej Stachowiak Sent: Wednesday, June 30, 2010 9:30 PM To: Brendan Eich Cc: Mark S. Miller; Jürg Lehni; Erik Arvidsson; es-discuss Subject: Re: Syntax Proposal: Allow Java-like Object Literals after constructor calls to set properties on created objects.
On Jun 30, 2010, at 9:09 PM, Brendan Eich wrote:
On Jun 30, 2010, at 7:37 PM, Mark S. Miller wrote:
And you're right that attribute-property-missing -> undefined -> false has an effect here. If we had kept the ES3 negative names, we could have defaulted to false and Erik (I think) would not find Object.create a mistake -- but then the high-integrity-by-default fans would be put out. Those fans should speak up if they care to defend against the "mistake" charge.
Fine. Had it defaulted to low integrity, that would have been a mistake. Erik & I know we disagree on this.
Allen seems to agree with Erik. Is this just a matter of personal opinion? The point that I don't see you responding to is that the Object.create defaults are opposite from what every other way of binding a property in the language uses.
Ok, that could be answered by arguing that Object.create needs different defaults for different use-cases from those other property-creating forms.
I think the fact that you have to go out of your way to get normal defaults is a considerable inconvenience in many situations. I think Object.create would be easier to deploy if its defaults matched other ways of creating properties.
, Maciej
On Wed, Jun 30, 2010 at 9:09 PM, Brendan Eich <brendan at mozilla.com> wrote:
On Jun 30, 2010, at 7:37 PM, Mark S. Miller wrote:
And you're right that attribute-property-missing -> undefined -> false has
an effect here. If we had kept the ES3 negative names, we could have defaulted to false and Erik (I think) would not find Object.create a mistake -- but then the high-integrity-by-default fans would be put out. Those fans should speak up if they care to defend against the "mistake" charge.
Fine. Had it defaulted to low integrity, that would have been a mistake. Erik & I know we disagree on this.
Allen seems to agree with Erik. Is this just a matter of personal opinion?
More that just personal opinion. At this point I think there is good evidence that Allen agrees with Erik ;).
The point that I don't see you responding to is that the Object.create defaults are opposite from what every other way of binding a property in the language uses.
Every other is two: 1) assignment, or 2) object literals. These already make low integrity easy. Prior to ES5, high integrity was impossible. A new API introduced to support high integrity should also make it easy -- if it's not easy to be safe, few will. Low integrity remains easy through assignment and object literals.
The agreed Harmony goal of "Provide syntactic support for high integrity patterns", as well as the repeatedly stated desires for some kind of class construct going back to ES4, shows that we did not go far enough in making high integrity easy. But we did what we could within the ES5 constraints. I'm glad we didn't do less. The easy way should be the safe way.
On Jul 1, 2010, at 2:13 AM, Mark S. Miller wrote:
On Wed, Jun 30, 2010 at 9:09 PM, Brendan Eich <brendan at mozilla.com> wrote: On Jun 30, 2010, at 7:37 PM, Mark S. Miller wrote:
And you're right that attribute-property-missing -> undefined -> false has an effect here. If we had kept the ES3 negative names, we could have defaulted to false and Erik (I think) would not find Object.create a mistake -- but then the high-integrity-by-default fans would be put out. Those fans should speak up if they care to defend against the "mistake" charge.
Fine. Had it defaulted to low integrity, that would have been a mistake. Erik & I know we disagree on this.
Allen seems to agree with Erik. Is this just a matter of personal opinion?
More that just personal opinion. At this point I think there is good evidence that Allen agrees with Erik ;).
LOL, pronoun trouble. I meant by "this" the whole "X is a mistake... !X is a mistake" approach, which seemed in your mail to come down to he-said/I-said ;-).
The point that I don't see you responding to is that the Object.create defaults are opposite from what every other way of binding a property in the language uses.
Every other is two: 1) assignment, or 2) object literals.
- Declaration by var/function -- makes non-configurable unless from eval, but writable.
- Pre-defined bindings are mostly writable -- built-in constructors' 'prototype' properties are a notable exception.
Writability rather than configurability or enumerability is the bone of contention, I think. Of course all three default the high-integrity way in Object.create, and I wouldn't want to split them up, some defaulting to true while others to false.
What I'm suggesting is that writable:false is what sticks out as less usable, especially for cases such as the Rhino object initializer extension recast using Object.create as in Allen's mail.
These already make low integrity easy. Prior to ES5, high integrity was impossible. A new API introduced to support high integrity should also make it easy -- if it's not easy to be safe, few will. Low integrity remains easy through assignment and object literals.
Yes, this was the rationale and I went along with it -- still do. I'm not jumping up and down about "mistake" here, rather trying to draw out exactly what is at issue. Calling Object.create "a mistake" in full is excessive, since it has other useful features that are not considered broken AFAIK.
The way to make progress (for future extensions, nothing's going to change in ES5) is to tease out the compositional, reduced or "primitive" missing semantics we may want to add to the language, and not condemn composite extensions wholesale. We can then argue about usability and convenience more clearly.
So my disagreement in the end (as in my initial followup!) is with Erik for condemning all of Object.create. There's good stuff in there, even if the defaults are less usable for some (more familiar, like what you can do in JS today) use-cases.
The agreed Harmony goal of "Provide syntactic support for high integrity patterns",
Don't see those words at harmony:harmony -- but I won't quibble :-|.
as well as the repeatedly stated desires for some kind of class construct going back to ES4, shows that we did not go far enough in making high integrity easy. But we did what we could within the ES5 constraints. I'm glad we didn't do less. The easy way should be the safe way.
As Allen said, the defaults being different make Object.create less usable for approximating extensions such as the Rhino your-constructor-call-with-args+object-initialiser-for-extra-properties one. That's clear enough without assuming anything beyond what was in Rhino's extension, or in ES1-3 or JS as practiced today -- i.e., without assuming that the user wants higher integrity, specifically non-writable properties.
Object.create has great defaults for creating built-in-like properties, akin to built-in constructor 'prototype', and of course for creating even more heavily armored objects. No question! ;-)
So, in the category "bleeding obvious" (I hope), and without beating the issues in this thread to death no further, my conclusion is that the Rhino extension or something that fills its usability gap it wants new syntax and not Object.create. I do not mean to imply either that we should add the Rhino extension (mutatis mutandis), or that Object.create is "a mistake".
Hope this helps,
On Thu, Jul 1, 2010 at 8:24 AM, Brendan Eich <brendan at mozilla.com> wrote:
On Jul 1, 2010, at 2:13 AM, Mark S. Miller wrote:
On Wed, Jun 30, 2010 at 9:09 PM, Brendan Eich <brendan at mozilla.com> wrote:
On Jun 30, 2010, at 7:37 PM, Mark S. Miller wrote:
And you're right that attribute-property-missing -> undefined -> false has
an effect here. If we had kept the ES3 negative names, we could have defaulted to false and Erik (I think) would not find Object.create a mistake -- but then the high-integrity-by-default fans would be put out. Those fans should speak up if they care to defend against the "mistake" charge.
Fine. Had it defaulted to low integrity, that would have been a mistake. Erik & I know we disagree on this.
Allen seems to agree with Erik. Is this just a matter of personal opinion?
More that just personal opinion. At this point I think there is good evidence that Allen agrees with Erik ;).
LOL, pronoun trouble. I meant by "this" the whole "X is a mistake... !X is a mistake" approach, which seemed in your mail to come down to he-said/I-said ;-).
The point that I don't see you responding to is that the Object.create defaults are opposite from what every other way of binding a property in the language uses.
Every other is two: 1) assignment, or 2) object literals.
- Declaration by var/function -- makes non-configurable unless from eval, but writable.
As we're moving away from object environment records, I take the world of variables and the world of properties to be distinct.
For variables in Harmony, we're introducing "const" and "let" and effectively deprecating "var". For a variable that's never assigned to, I suggest that "const" is better style. Then those variables declared with "let" stand out, signaling to the programmer "Assume this variable may be assigned to".
Named function expressions already bind the function name with a const (unassignable) binding. Regarding named function declarations, I find < strawman:const_functions> attractive.
IIRC, so do you. I've just added a "High Integrity Factories" example to the end of that page. That example does demonstrate my one regret about the definition of Object.create() -- that it defaults to creating an extensible object, requiring a separate Object.freeze() to complete the second example. Because of this "mistake", the complete second example is no longer pleasantly terse.
- Pre-defined bindings are mostly writable -- built-in constructors' 'prototype' properties are a notable exception.
Writability rather than configurability or enumerability is the bone of contention, I think. Of course all three default the high-integrity way in Object.create, and I wouldn't want to split them up, some defaulting to true while others to false.
Glad to hear it. Our positions are closer than I'd feared. My main concern in the design was ensuring that configurability defaulted to false. Besides integrity, this should also enable various optimizations. When combined with non-extensibility, it gives us objects of fixed state. That said, I still think writable defaulting false was the right decision.
What I'm suggesting is that writable:false is what sticks out as less usable, especially for cases such as the Rhino object initializer extension recast using Object.create as in Allen's mail.
OTOH, the Classes as Sugar to-be-strawman at < esdiscuss/2009-March/009115>
expands to defining frozen instances. To get the scoping benefit that Peter Michaux suggested -- where public instance members are also in scope as variables in the defining context -- to work for mutable variables/instance fields, the only non-with-way to do that is to have the instance fields be defined as accessors accessing the variable. This is consistent with the instance being frozen.
These already make low integrity easy. Prior to ES5, high integrity was impossible. A new API introduced to support high integrity should also make it easy -- if it's not easy to be safe, few will. Low integrity remains easy through assignment and object literals.
Yes, this was the rationale and I went along with it -- still do. I'm not jumping up and down about "mistake" here, rather trying to draw out exactly what is at issue. Calling Object.create "a mistake" in full is excessive, since it has other useful features that are not considered broken AFAIK.
The way to make progress (for future extensions, nothing's going to change in ES5) is to tease out the compositional, reduced or "primitive" missing semantics we may want to add to the language, and not condemn composite extensions wholesale. We can then argue about usability and convenience more clearly.
So my disagreement in the end (as in my initial followup!) is with Erik for condemning all of Object.create. There's good stuff in there, even if the defaults are less usable for some (more familiar, like what you can do in JS today) use-cases.
The agreed Harmony goal of "Provide syntactic support for high integrity patterns",
Don't see those words at harmony:harmony -- but I won't quibble :-|.
See harmony:harmony#means 2.II.
s/support/conveniences/
as well as the repeatedly stated desires for some kind of class construct going back to ES4, shows that we did not go far enough in making high integrity easy. But we did what we could within the ES5 constraints. I'm glad we didn't do less. The easy way should be the safe way.
As Allen said, the defaults being different make Object.create less usable for approximating extensions such as the Rhino your-constructor-call-with-args+object-initialiser-for-extra-properties one. That's clear enough without assuming anything beyond what was in Rhino's extension, or in ES1-3 or JS as practiced today -- i.e., without assuming that the user wants higher integrity, specifically non-writable properties.
Object.create has great defaults for creating built-in-like properties, akin to built-in constructor 'prototype', and of course for creating even more heavily armored objects. No question! ;-)
So, in the category "bleeding obvious" (I hope), and without beating the issues in this thread to death no further, my conclusion is that the Rhino extension or something that fills its usability gap it wants new syntax and not Object.create. I do not mean to imply either that we should add the Rhino extension (mutatis mutandis), or that Object.create is "a mistake".
Hope this helps,
It helps a lot. Thanks. Our positions are indeed fairly close.
On Thu, Jul 1, 2010 at 11:13 AM, Mark S. Miller <erights at google.com> wrote:
On Thu, Jul 1, 2010 at 8:24 AM, Brendan Eich <brendan at mozilla.com> wrote:
On Jul 1, 2010, at 2:13 AM, Mark S. Miller wrote:
On Wed, Jun 30, 2010 at 9:09 PM, Brendan Eich <brendan at mozilla.com>wrote:
On Jun 30, 2010, at 7:37 PM, Mark S. Miller wrote:
And you're right that attribute-property-missing -> undefined -> false
has an effect here. If we had kept the ES3 negative names, we could have defaulted to false and Erik (I think) would not find Object.create a mistake -- but then the high-integrity-by-default fans would be put out. Those fans should speak up if they care to defend against the "mistake" charge.
Fine. Had it defaulted to low integrity, that would have been a mistake. Erik & I know we disagree on this.
Allen seems to agree with Erik. Is this just a matter of personal opinion?
More that just personal opinion. At this point I think there is good evidence that Allen agrees with Erik ;).
LOL, pronoun trouble. I meant by "this" the whole "X is a mistake... !X is a mistake" approach, which seemed in your mail to come down to he-said/I-said ;-).
The point that I don't see you responding to is that the Object.create defaults are opposite from what every other way of binding a property in the language uses.
Every other is two: 1) assignment, or 2) object literals.
- Declaration by var/function -- makes non-configurable unless from eval, but writable.
As we're moving away from object environment records, I take the world of variables and the world of properties to be distinct.
For variables in Harmony, we're introducing "const" and "let" and effectively deprecating "var". For a variable that's never assigned to, I suggest that "const" is better style. Then those variables declared with "let" stand out, signaling to the programmer "Assume this variable may be assigned to".
Named function expressions already bind the function name with a const (unassignable) binding. Regarding named function declarations, I find < strawman:const_functions> attractive. IIRC, so do you. I've just added a "High Integrity Factories" example to the end of that page. That example does demonstrate my one regret about the definition of Object.create() -- that it defaults to creating an extensible object, requiring a separate Object.freeze() to complete the second example. Because of this "mistake", the complete second example is no longer pleasantly terse.
- Pre-defined bindings are mostly writable -- built-in constructors' 'prototype' properties are a notable exception.
Writability rather than configurability or enumerability is the bone of contention, I think. Of course all three default the high-integrity way in Object.create, and I wouldn't want to split them up, some defaulting to true while others to false.
Glad to hear it. Our positions are closer than I'd feared. My main concern in the design was ensuring that configurability defaulted to false. Besides integrity, this should also enable various optimizations. When combined with non-extensibility, it gives us objects of fixed state.
I meant "fixed shape". An especially confusing typo in this context, sorry.
A lot of discussion since I last checked in...
I apologize for the harshness. I do agree that Object.create is useful and I agree that the default values for the property descriptor is acceptable even though having read only be the default is painful but consistency trumps that one.
My main issue is actually that Object.create is such a low level construct. It is very good for library authors to hide it behind a friendlier interface. I wish Object.create was called something more obscure and Object.create was targeting end users. Instead of passing a map of property descriptors an object would be passed. I just find the extra layer of object literals too much.
var o = Object.create(Foo.prototype, { _a: { value: 1, writable: true, }, a: { get: function() { return this._a; }, set: function(a) { this._a = a; }, enumerable: true } };
If Object.create was created with users in mind it might have looked something like:
var o = Object.create(Foo.prototype, { _a: 1, get a() { return this._a; }, set a(a) { this._a = a; } });
Looking forward, I like where Allen is going with the extended object literals. Today, we can do the following in most browsers (relying on the proto extension).
var o = { proto: Foo.prototype, _a: 1, get a() { return this._a; }, set a(a) { this._a = a; } };
This is clearly a usability win, even with the ugly underscores. With Allens proposal that would become:
var o = { [proto: Foo.prototype] _a: 1, get a() { return this._a; }, set a(a) { this._a = a; } };
From: Erik Arvidsson This is clearly a usability win, even with the ugly underscores. With Allens proposal that would become:
var o = { [proto: Foo.prototype] _a: 1, get a() { return this._a; }, set a(a) { this._a = a; } };
Or in combination with Dave Herman's names proposal something like:
private _a; var o = { [proto: Foo.prototype] _a: 1, get a() { return this._a; }, set a(a) { this._a = a; } };
Or
var o = { [proto: Foo.prototype, private: _ a] _a: 1, get a() { return this._a; }, set a(a) { this._a = a; } };
Or maybe even just:
var o = { [proto: Foo.prototype] private _a: 1, get a() { return this._a; }, set a(a) { this._a = a; } };
On Thu, Jul 1, 2010 at 12:56, Allen Wirfs-Brock <Allen.Wirfs-Brock at microsoft.com> wrote:
var o = { [proto: Foo.prototype] private _a: 1, get a() { return this._a; }, set a(a) { this._a = a; } };
I like.
Brendan, just to clarify:
the Rhino your-constructor-call-with-args+object-initialiser-for-extra-properties
Rhino does not use the object literal following a constructor call as an initialiser for extra properties. It just passes it to the constructor by adding it to the end of the list of arguments already passed to it. The proposal to use it as an initialiser comes from me tinkering with it on the Rhino proxy object level by making it automatically initialise the created instances. I came to the conclusion that such a use of it is very handy without realising this was proposed years ago already, as you pointed out in your previous email.
Do you think something along these lines, or the mentioned 'merge / mixin' operator could be discussed to become a future standard?
Jürg
On Thu, Jul 1, 2010 at 2:31 PM, Jürg Lehni <lists at scratchdisk.com> wrote:
Brendan, just to clarify:
the Rhino your-constructor-call-with-args+object-initialiser-for-extra-properties
Rhino does not use the object literal following a constructor call as an initialiser for extra properties. It just passes it to the constructor by adding it to the end of the list of arguments already passed to it. The proposal to use it as an initialiser comes from me tinkering with it on the Rhino proxy object level by making it automatically initialise the created instances. I came to the conclusion that such a use of it is very handy without realising this was proposed years ago already, as you pointed out in your previous email.
Do you think something along these lines, or the mentioned 'merge / mixin' operator could be discussed to become a future standard?
It is certainly in bounds. Other proposals for perhaps the same "niche" if you will include the aforementioned classes-as-sugar, Allen's enhanced object literals, or more direct support for traits (see traitsjs.org).
My sense is that all of these, and perhaps your upcoming proposal, all have enough overlap in motivation that only one should be included. My own preference order is 1) traits, 2) classes-as-sugar, 3) enhanced object literals. I consider all of these choices to be viable starting points. Only .#1 addresses your desire for mixin-like functionality.
If you agree that your proposal would be, if accepted, an alternative to these other three, it would be helpful to try stating some pros and cons compared to these others. Thanks.
Jürg Lehni wrote:
On 30 Jun 2010, at 19:13, Brendan Eich wrote:
Some of us old-timers were around at Netscape with Norris Boyd when this was designed and added to Rhino. It was not added to SpiderMonkey, though, for not terribly compelling reasons.
Do you remember what the reason was why this was added to Rhino in the first place? Was it to facilitate Java-like syntax when implementing interfaces? I remember seeing examples with such code quite a while ago, triggering me playing around with the feature and finding out that the object is simply passed as a last added argument. I did not manage to find any example code that somehow makes reference to this recently though, so I am wondering if the Rhino people are maybe trying to get rid of this too on the long term? After all it is not proper ES syntax, and for example Eclipse's syntax checker is complaining about it.
I remember being concerned about the ASI issue, which requries a [no LineTerminator here] restricted production. That is still a concern: the TC39 committee doesn't want to add restricted productions without very good reason.
This came up later, via bugzilla.mozilla.org/show_bug.cgi?id=253138 -- see bugzilla.mozilla.org/show_bug.cgi?id=253138#c7. bugzilla.mozilla.org/show_bug.cgi?id=253138#c10 suggests an operator of some sort to "merge" the objects.
Another point to note is that [1,2,3] is not equivalent to new Array(3) {0:1, 1:2, 2:3}, because the array initialiser form users the original value of Array.prototype, and it does not call the current binding of Array as a constructor.
So, more work needed to avoid a restricted production, at least. A linking operator or keyword ("with" a la functional record update) would help.
This is all very interesting. An operator would make the feature even more useful, as it would not be bound to the calling of a constructor. Could a simple : work? E.g.:
new Array(3) : { 0:1, 1:2, 2:3 };
Jürg
boolVal ? new Array(3) : { 0:1, 1:2, 2:3 };
Completely different result... I think that syntax would cause confusion.
This simple proposal is inspired by an extension of Rhino that currently allows to implement its syntax for anonymous Java interface implementation. Here an example that creates an anonymous class implementing the Runnable interface and defining the run method in an anonymous object literal that (mimicking a Java code block) immediately following the constructor call:
var runnable = new java.lang.Runnable() { run: function() { } };
When looking deeper into how Rhino achieves this syntax, I found out that it simply appends the following anonymous object literal to the list of arguments of whatever constructor came before. So the following code works in Rhino and prints the content of the hello string to the console:
function Test(obj) { print(obj.hello); }
new Test() { hello: 'Greetings, I am an anonymous object literal' };
For the Illustrator scripting plugin scriptographer.org I came up with the convention to (ab)use this non-standard feature to allow setting of properties on freshly created objects, by extending the underlying Java proxy objects to automatically detect such a passed object literal, iterate through its properties and set them on the newly created object (In Scriptographer it is then also removed from the argument list). Soon it became apparent that this is very useful and also leads to cleaner code. I therefore started to wonder if this would make sense as an syntax extension in ES5. Here another example.
function MyConstructor(param) { print(param); // Should not print the object literal }
var obj = new MyConstructor() { property: 'This will be automatically set on the created object' };
print(obj.property); // 'This will...created object'
So far I cannot see any syntax conflicts.
I am wondering what you all think of this proposal and look forward to your thoughts.
Jürg