New Object Extension Literal Strawman

# Allen Wirfs-Brock (12 years ago)

At last week's T39 meeting there appeared to be significant support for some form of the "Mustache" (or "Monocle") operator for literally batching property manipulations on preexisting objects. Previously, this extension did not have its own strawman so I was requested to produce a separate revised strawman for Object Extension Literals. The new strawman proposal is now at strawman:object_extension_literals It also incorporates concepts for Dave Hermans "batch assignment operator" strawman.

# Nicholas C. Zakas (12 years ago)

Is it intentional that the first example uses colons between the property and value while all others use the equals sign?

obj.{a:1,b:2,c:3};

vs.

obj.{a=1,b=2,c=3};

Thanks, Nicholas

# Erik Arvidsson (12 years ago)

On Mon, May 28, 2012 at 12:19 PM, Nicholas C. Zakas <standards at nczconsulting.com> wrote:

Is it intentional that the first example uses colons between the property and value while all others use the equals sign?

Yes this is intentional. We discussed this a bit at the F2F meeting and we came up with the proposal to use = for [[Put]] and : for [[DefineOwnProperty]]. Having a way to do [[Put]] is important because it is common to want to trigger setters.

For example:

class Monster { set health(value) { this.barColor = value < 10 ? 'red' : 'green'; this._health = value; } }

class GiantSpider extends Monster { constructor() { this.{ health = 100 } } }

If you use .{health: 100} then the setter would not be invoked and the object would not be in a correct state. This example might be a bit contrived but using [[DefineOwnProperty]] is not the right answer in a lot of cases.

# Allen Wirfs-Brock (12 years ago)

On May 28, 2012, at 1:23 PM, Erik Arvidsson wrote:

On Mon, May 28, 2012 at 12:19 PM, Nicholas C. Zakas <standards at nczconsulting.com> wrote:

Is it intentional that the first example uses colons between the property and value while all others use the equals sign?

Yes this is intentional. We discussed this a bit at the F2F meeting and we came up with the proposal to use = for [[Put]] and : for [[DefineOwnProperty]]. Having a way to do [[Put]] is important because it is common to want to trigger setters.

At added a clarification of this to the strawman....

For example:

class Monster { set health(value) { this.barColor = value < 10 ? 'red' : 'green'; this._health = value; } }

class GiantSpider extends Monster { constructor() { this.{ health = 100 } } }

If you use .{health: 100} then the setter would not be invoked and the object would not be in a correct state. This example might be a bit contrived but using [[DefineOwnProperty]] is not the right answer in a lot of cases.

This is especially important for setting values of DOM node properties which are generally implemented as accessor properties (ie, getters/setters). That is why I used (thanks to dherman) a DOM element in most of the example that use +.

# Nicholas C. Zakas (12 years ago)

Thanks for the clarifications. I understand wanting to make the distinction from an implementer point of view, I'm just not sure that developers make the distinction between [[Put]] and [[DefineOwnProperty]] even using regular object literals. Is there a reason that this:

var obj = {}; obj.{ name: "JS" };

Can't act the same as this:

var obj = {}; obj.name = "JS";

Wherein, if the property is already defined that acts as [[Put]] and otherwise acts as [[DefineOwnProperty]]?

Thanks, Nicholas

# Tab Atkins Jr. (12 years ago)

On Tue, May 29, 2012 at 9:26 AM, Nicholas C. Zakas <standards at nczconsulting.com> wrote:

Thanks for the clarifications. I understand wanting to make the distinction from an implementer point of view, I'm just not sure that developers make the distinction between [[Put]] and [[DefineOwnProperty]] even using regular object literals. Is there a reason that this:

var obj = {}; obj.{    name: "JS" };

Can't act the same as this:

var obj = {}; obj.name = "JS";

Wherein, if the property is already defined that acts as [[Put]] and otherwise acts as [[DefineOwnProperty]]?

Same thoughts here. Regular devs (like me!) only see [[DefineOwnProperty]] when creating a literal, and then there's no observable distinction between these two in normal circumstances:

var obj = { foo: bar }

var obj = {}; obj.foo = bar;

I don't think it's worth optimizing for the [[DefineOwnProperty]] case. We should just use the standard "foo:bar" syntax, and have it invoke [[Put]] behavior.

As written, the strawman's attachment of the more natural "foo:bar" syntax for [[DefineOwnProperty]] is a footgun.

# Herby Vojčík (12 years ago)

Tab Atkins Jr. wrote:

On Tue, May 29, 2012 at 9:26 AM, Nicholas C. Zakas <standards at nczconsulting.com> wrote:

Thanks for the clarifications. I understand wanting to make the distinction from an implementer point of view, I'm just not sure that developers make the distinction between [[Put]] and [[DefineOwnProperty]] even using regular object literals. Is there a reason that this:

var obj = {}; obj.{ name: "JS" };

Can't act the same as this:

var obj = {}; obj.name = "JS";

You forget the

obj.{ conciseMethod() {}, get x() {}, set x(v) {} }

probably. Or you want the special case for prop:value?

# Brendan Eich (12 years ago)

Nicholas C. Zakas wrote:

Thanks for the clarifications. I understand wanting to make the distinction from an implementer point of view, I'm just not sure that developers make the distinction between [[Put]] and [[DefineOwnProperty]] even using regular object literals. Is there a reason that this:

var obj = {}; obj.{ name: "JS" };

Can't act the same as this:

var obj = {}; obj.name = "JS";

Wherein, if the property is already defined that acts as [[Put]] and otherwise acts as [[DefineOwnProperty]]?

There's a big reason, which came up recently as if it were still exploitable in modern browsers (fixed in 2009 era, at least in Firefox):

fafadiatech.blogspot.de/2012/04/json-hijacking.html

SpiderMonkey bug fixed in 2009:

bugzilla.mozilla.org/show_bug.cgi?id=474501

Note that the modern DOM and WebIDL are being spec'ed so typical DOM accessors are on prototypes, so you can't "fix" the bug by using [[Put]] only when the property is "own".

There really is a difference between assigning and defining in JS. Assigning runs setters, even inherited ones. Defining overrides or shadows. EIBTI.

# Mark S. Miller (12 years ago)

The use of foo:bar in object literals and in JSON causes [[DefineOwnProperty]] behavior, not [[Put]] behavior. Making mustache's treatment of foo:bar inconsistent with object literals and with JSON is a worse footgun. If you always want mustache-with-[[Put]], use "=" rather than ":".

# Nicholas C. Zakas (12 years ago)

On 5/29/2012 9:45 AM, Brendan Eich wrote:

Note that the modern DOM and WebIDL are being spec'ed so typical DOM accessors are on prototypes, so you can't "fix" the bug by using [[Put]] only when the property is "own".

There really is a difference between assigning and defining in JS. Assigning runs setters, even inherited ones. Defining overrides or shadows. EIBTI.

Once again, I'm not debating whether there is or isn't a difference, just whether or not that difference is perceivable to regular developers. My theory is that it is not, and so you'll end up with a lot of code that looks like this:

var element = document.getElementById("myDiv"); element.{ innerHTML: "Hello world!", className: "enabled" };

Going by the current definition, this code block would actually have no effect because it would be defining new own properties rather than assigning values to the existing prototype accessor properties. I cringe at the thought of how long it would take most developers to figure out that it is the colon that's the problem. IMHO, This code doesn't behave as expected.

# Allen Wirfs-Brock (12 years ago)

On May 29, 2012, at 9:33 AM, Tab Atkins Jr. wrote:

On Tue, May 29, 2012 at 9:26 AM, Nicholas C. Zakas <standards at nczconsulting.com> wrote:

Thanks for the clarifications. I understand wanting to make the distinction from an implementer point of view,

It isn't really an implementation issue. This concerns some of the core semantics of the language. Implementors have to implement those semantics no mater how inconvenient it might be.

I'm just not sure that developers make the distinction between [[Put]] and [[DefineOwnProperty]] even using regular object literals. Is there a reason that this:

var obj = {}; obj.{ name: "JS" };

Can't act the same as this:

var obj = {}; obj.name = "JS";

Wherein, if the property is already defined that acts as [[Put]] and otherwise acts as [[DefineOwnProperty]]?

Same thoughts here. Regular devs (like me!) only see [[DefineOwnProperty]] when creating a literal, and then there's no observable distinction between these two in normal circumstances:

var obj = { foo: bar }

var obj = {}; obj.foo = bar;

But in fact, there is already such an observable difference, if the prototype of obj already has a property named "foo". In these simple examples, the prototype is is Object.prototype but it more general cases (especially if we have class declarations) the object that is being extend is arbitrary.

Some cases where it a difference:

Object.defineProperty(Object.prototype, "foo", {set: function(v) {console.log(v]"hello: ",v)}, configurable: true});

var obj = {foo: bar}; //creates a own data property on obj that is initialize to the value of var

var obj={}; obj.foo = bar; //does not create a property, produces console output: "hello: "+bar

Object.defineProperty(Object.prototype, "foo", {value: "can't change me", writable: false, configurable: true});

var obj = {foo: bar}; //creates a own data property on obj that is initialize to the value of var

var obj={}; obj.foo = bar; //does not create a property, because inherited property is read-only

(function() {"use strict"; obj.foo = bar;})(); //throws a ReferenceError

I don't think it's worth optimizing for the [[DefineOwnProperty]] case. We should just use the standard "foo:bar" syntax, and have it invoke [[Put]] behavior.

As written, the strawman's attachment of the more natural "foo:bar" syntax for [[DefineOwnProperty]] is a footgun.

ES 5 defines "foo:bar" as meaning [[DefineOwnPropertuy]]. Prior to the standardization of getter/setter semantics in ES5 some implementation used [[Put]] for "foo:bar". That was identified as a potentially (perhaps even actually) exploitable security issue. The semantics of "foo:bar" within an object literal is not going to change back to [[Put]]

I'm seen many concerns raised about consistency within JS. Having {foo:bar} and obj.{foo:bar} behave inconsistently doesn't seem like a very good idea.

# Tab Atkins Jr. (12 years ago)

On Tue, May 29, 2012 at 9:45 AM, Mark S. Miller <erights at google.com> wrote:

The use of foo:bar in object literals and in JSON causes [[DefineOwnProperty]] behavior, not [[Put]] behavior. Making mustache's treatment of foo:bar inconsistent with object literals and with JSON is a worse footgun. If you always want mustache-with-[[Put]], use "=" rather than ":".

My and Nicholas' point is that normal developers never see the difference between these two. Normal devs don't define setters on Object.prototype, so the fact that "var obj = {foo:bar};" is different from "var obj = {}; obj.foo = bar;" is completely lost on them.

Given that these two are identical to most devs, giving them distinct syntaxes in the strawman is problematic. Further, and much more importantly, [[DefineOwnProperty]] is nearly never the behavior that a dev will actually want. Devs are not going to be replacing all their Object.defineProperties() calls with monocle-mustache, because no one actually uses that function in normal production, so there's nothing to replace. Normal devs do a lot of property assigning, though, and that's the use-case we're targetting with this.

If it's desperately important that we don't make "obj.{foo:bar}" do a [[Put]], then it would be much better to simply not allow that syntax at all. Make monocle-mustache solely use the "obj.{foo=bar}" syntax, so that the other one is a syntax error. There's no good reason to give devs the other type of functionality besides completeness, and using it will almost always be a mistake on the dev's part, I think.

Is the "setter on Object.prototype" the only observable difference between the two?

# T.J. Crowder (12 years ago)

On 29 May 2012 17:59, Nicholas C. Zakas <standards at nczconsulting.com> wrote:

var element = document.getElementById("**myDiv"); element.{ innerHTML: "Hello world!", className: "enabled" };

Going by the current definition, this code block would actually have no effect because it would be defining new own properties rather than assigning values to the existing prototype accessor properties. I cringe at the thought of how long it would take most developers to figure out that it is the colon that's the problem. IMHO, This code doesn't behave as expected.

It'll throw, right? If so, presumably the developer wouldn't take too long to figure it out.

What would your fix be? To reverse : and = in the new syntax, so that : means [[Put]] and = means [[DefineOwnProperty]]? Because Brendan's point about exploits is a cogent one, and as : means [[DefineOwnProperty]] in literals today, it seems like sticking with that makes sense. Developers using the new syntax will hopefully learn the importance of using =. Especially as I would expect that it would be the default choice for most people, most of the time, so that's what they'd see as examples... (I could be wrong there, of course.)

-- T.J.

# Brendan Eich (12 years ago)

Nicholas C. Zakas wrote:

On 5/29/2012 9:45 AM, Brendan Eich wrote:

Note that the modern DOM and WebIDL are being spec'ed so typical DOM accessors are on prototypes, so you can't "fix" the bug by using [[Put]] only when the property is "own".

There really is a difference between assigning and defining in JS. Assigning runs setters, even inherited ones. Defining overrides or shadows. EIBTI. Once again, I'm not debating whether there is or isn't a difference, just whether or not that difference is perceivable to regular developers.

I'm not only saying there should be a difference (because JSON theft hole re-opens, at least for mustache; and we want mustache and object literal semantics to match for the same given syntax), but that regular developers need to know what they're doing. This is true in JS today if a "regular" developer ever needs to shadow or override rather than assign.

My theory is that it is not, and so you'll end up with a lot of code that looks like this:

var element = document.getElementById("myDiv"); element.{ innerHTML: "Hello world!", className: "enabled" };

I hear you, and having a choice means people can choose badly. This was one reason why => succeeded at the price of cutting ->. It's not an

absolutely fatal objection, though. Three points:

  1. First, don't people test? You say we'll end up with a lot of code like that, but such code won't work as intended. Developers may "start up" like that and learn, then change and adapt. Again, there's no iron-clad theory governing practice here, and people are smart about learning what works.

  2. With => the default is fail-safer (lexical this) compared to -> (dynamic this). Anyone wanting -> and using => by mistake has a bug but

it can be found via testing more readily than a dynamic-this bug. In other words, we want the failure mode to be a more overt bug, if it can't be an early error.

  1. If we changed mustache to diverge from object literals, then developers who refactor or otherwise end up needing to define not assign will be out of luck. This is not obviously fatal either, but it counts against implicit sometimes-assign-sometimes-define semantics, IMHO.

Going by the current definition, this code block would actually have no effect because it would be defining new own properties rather than assigning values to the existing prototype accessor properties.

Yes, and there would not be any "Hello world!" in the div's text content, and wouldn't the developer see that and fix?

Again I hear a lot of "we'll end up with permanent bugs" objections (same thing for => not ->) but these fears presume lack of testing for

the more overt failure than you'd "end up" with the other way (if we had -> not => semantics; or here, if we have = not : when the property

exists in or own).

I cringe at the thought of how long it would take most developers to figure out that it is the colon that's the problem. IMHO, This code doesn't behave as expected.

What is the expectation based on? Object.extend in prototype? That is a fair point, but that function copies properties by assignment.

# Brendan Eich (12 years ago)

Allen Wirfs-Brock wrote:

ES 5 defines "foo:bar" as meaning [[DefineOwnPropertuy]]. Prior to the standardization of getter/setter semantics in ES5 some implementation used [[Put]] for "foo:bar".

They did that because ES3 mandated it!

This was indeed a bug in ES3.

That was identified as a potentially (perhaps even actually) exploitable security issue.

Actually. See

hackademix.net/2009/01/13/you-dont-know-what-my-twitter-leaks

prefigured by posts such as

jeremiahgrossman.blogspot.com/2006/01/advanced-web-attack-techniques-using.html

# Brendan Eich (12 years ago)

Tab Atkins Jr. wrote:

Make monocle-mustache solely use the "obj.{foo=bar}" syntax, so that the other one is a syntax error. There's no good reason to give devs the other type of functionality besides completeness, and using it will almost always be a mistake on the dev's part, I think.

Who says obj is an Object instance delegating only to Object.prototype?

Is the "setter on Object.prototype" the only observable difference between the two?

Not for mustache, where obj can be any object, with a deep prototype chain, setters declared via ES5-standard syntax (no definePropert{y,ies}), or created by an OOP library.

As Allen points out, classes will (let's hope) allow easy declaration of deep chains too.

Beware scenario-solving in an all-or-nothing way. There's no absolute good here, and while people may (due to Prototype's Object.extend) want = not : in most cases, that doesn't mean : is unnecessary.

Another alternative: give up on mustache since it bends object literal syntax past its breaking point.

If we just standardized some flavor of Object.extend (for-in based? own or in test for properties to copy? no private names? etc.) would developers be better off?

# Allen Wirfs-Brock (12 years ago)

On May 29, 2012, at 10:15 AM, Brendan Eich wrote:

Allen Wirfs-Brock wrote:

ES 5 defines "foo:bar" as meaning [[DefineOwnPropertuy]]. Prior to the standardization of getter/setter semantics in ES5 some implementation used [[Put]] for "foo:bar".

They did that because ES3 mandated it!

Yes, but it wan't a significant issue until setter properties were added as a non-standardized (pre-standardizaton?) extension ...

# Brendan Eich (12 years ago)

Allen Wirfs-Brock wrote:

On May 29, 2012, at 10:15 AM, Brendan Eich wrote:

Allen Wirfs-Brock wrote:

ES 5 defines "foo:bar" as meaning [[DefineOwnPropertuy]]. Prior to the standardization of getter/setter semantics in ES5 some implementation used [[Put]] for "foo:bar". They did that because ES3 mandated it!

Yes, but it wan't a significant issue until setter properties were added as a non-standardized (pre-standardizaton?) extension ...

Still, your words let ES3 completely off the hook, and setters went in before ES3 was truly finalized. It was more like a race:

$ cvs annotate -r3.4 jsopcode.tbl | grep SETTER 3.5 (brendan% 21-Sep-99): OPDEF(JSOP_SETTER,
124,js_setter_str,js_setter_str,1, 0, 0, 0, JOF_BYTE)

# Axel Rauschmayer (12 years ago)

Beware scenario-solving in an all-or-nothing way. There's no absolute good here, and while people may (due to Prototype's Object.extend) want = not : in most cases, that doesn't mean : is unnecessary.

Define properties manually when you really need it? I agree with Zakas et al.: it will be nearly impossible to teach. I have a hard time telling the two apart, too.

Another alternative: give up on mustache since it bends object literal syntax past its breaking point.

I see two options:

  1. Only do =. How will method definitions be done?
  2. Only do :, but with [[Put]].
# Tab Atkins Jr. (12 years ago)

On Tue, May 29, 2012 at 10:21 AM, Brendan Eich <brendan at mozilla.org> wrote:

Tab Atkins Jr. wrote:

Make monocle-mustache solely use the "obj.{foo=bar}" syntax, so that the other one is a syntax error.  There's no good reason to give devs the other type of functionality besides completeness, and using it will almost always be a mistake on the dev's part, I think.

Who says obj is an Object instance delegating only to Object.prototype?

Is the "setter on Object.prototype" the only observable difference between the two?

Not for mustache, where obj can be any object, with a deep prototype chain, setters declared via ES5-standard syntax (no definePropert{y,ies}), or created by an OOP library.

As Allen points out, classes will (let's hope) allow easy declaration of deep chains too.

Beware scenario-solving in an all-or-nothing way. There's no absolute good here, and while people may (due to Prototype's Object.extend) want = not : in most cases, that doesn't mean : is unnecessary.

I'm not making the assumption that "obj" in the examples is necessarily an Object with Object.prototype. It's irrelevant to the two points I'm trying to make:

  1. The use-case for mustache is to replace current code that does a lot of property-assigning statements with a more concise expression.

  2. Devs will generally be surprised by [[DefineOwnProperty]] behavior, since it's not observably different from [[Put]] in current code except in some degenerate cases that aren't seen in non-adversarial scenarios.

I would also assert that there's not a strong use-case for giving [[DefineOwnProperty]] substantially more concise syntax. (I'd agree with a somewhat terser API than the current Object.defineProperties(), but I don't think it needs actual syntax support.)

When you do have a deep prototype chain, with details possibly buried deep in code that you don't want to look at, how often do you actually want to completely replace a given property? How often is that a good behavior that won't cause accidental bugs due to setters not being called? We switched to [[DefineOwnProperty]] for literals to avoid a specific attack scenario that is extremely unlikely to have an effect on real code, because authors don't normally adjust Object.prototype and thus don't expect anything weird to happen when they create literals. This assumption might change with classes.

Another alternative: give up on mustache since it bends object literal syntax past its breaking point.

If we just standardized some flavor of Object.extend (for-in based? own or in test for properties to copy? no private names? etc.) would developers be better off?

I think we need a chainable expression form of property assigning. How the syntax looks isn't overly important, as long as it's chainable and an expression. ^_^ The "chainable" part, though, probably means that we do need actual syntax for it, rather than adding a new function.

# Brendan Eich (12 years ago)

Axel Rauschmayer wrote:

Beware scenario-solving in an all-or-nothing way. There's no absolute good here, and while people may (due to Prototype's Object.extend) want = not : in most cases, that doesn't mean : is unnecessary.

Define properties manually when you really need it? I agree with Zakas et al.: it will be nearly impossible to teach. I have a hard time telling the two apart, too.

Another alternative: give up on mustache since it bends object literal syntax past its breaking point.

I see two options:

  1. Only do =. How will method definitions be done?

That's the least of it!

You said "impossible to teach", yet under your (1) here, you'd be teaching new, bespoke syntax that looks a bit like object literals but has differences both syntactic (= not :) and semantic (assign not define).

If you can teach a new song to a musically inclined child, why not teach them the song Allen proposed?

If you can't teach what Allen proposed but you can teach only = not : then teach the subset and leave : for the more advanced students.

  1. Only do :, but with [[Put]].

This is a non-starter in TC39.

Even ignoring the inconsistency with object literals, (2) may well result in exploits down the road. History does not repeat but it rhymes.

# Allen Wirfs-Brock (12 years ago)

On May 29, 2012, at 10:08 AM, Tab Atkins Jr. wrote:

On Tue, May 29, 2012 at 9:45 AM, Mark S. Miller <erights at google.com> wrote:

The use of foo:bar in object literals and in JSON causes [[DefineOwnProperty]] behavior, not [[Put]] behavior. Making mustache's treatment of foo:bar inconsistent with object literals and with JSON is a worse footgun. If you always want mustache-with-[[Put]], use "=" rather than ":".

If it's desperately important that we don't make "obj.{foo:bar}" do a [[Put]], then it would be much better to simply not allow that syntax at all. Make monocle-mustache solely use the "obj.{foo=bar}" syntax, so that the other one is a syntax error. There's no good reason to give devs the other type of functionality besides completeness, and using it will almost always be a mistake on the dev's part, I think.

That are a lot of other use cases for Object extension literals. See the proposal for some examples. If you only allow "foo=bar" you loose them.

Language design and extension is a complex process. Being supportive of multiple use cases generally makes a new feature more valuable and more likely to be adopted. It also makes for smaller, more consistent languages. Socially, support of multiple use cases helps a feature achieve consensus support. A "mustache" proposal that only allows "foo=bar" may not do enough to get the support behind it that is needed to make the cut. Neither might a proposal that lacks "foo=bar". The fact that there are multiple perspectives isn't bad, and it doesn't mean that we are "designing by committee". It is simply how language design works.

Finally, I'm skeptical of any self-styled representation of "normal developers" that isn't backed up with data. In particular,

# Brendan Eich (12 years ago)

Tab Atkins Jr. wrote:

On Tue, May 29, 2012 at 10:21 AM, Brendan Eich<brendan at mozilla.org> wrote:

Tab Atkins Jr. wrote:

Make monocle-mustache solely use the "obj.{foo=bar}" syntax, so that the other one is a syntax error. There's no good reason to give devs the other type of functionality besides completeness, and using it will almost always be a mistake on the dev's part, I think. Who says obj is an Object instance delegating only to Object.prototype?

Is the "setter on Object.prototype" the only observable difference between the two? Not for mustache, where obj can be any object, with a deep prototype chain, setters declared via ES5-standard syntax (no definePropert{y,ies}), or created by an OOP library.

As Allen points out, classes will (let's hope) allow easy declaration of deep chains too.

Beware scenario-solving in an all-or-nothing way. There's no absolute good here, and while people may (due to Prototype's Object.extend) want = not : in most cases, that doesn't mean : is unnecessary.

I'm not making the assumption that "obj" in the examples is necessarily an Object with Object.prototype. It's irrelevant to the two points I'm trying to make:

  1. The use-case for mustache is to replace current code that does a lot of property-assigning statements with a more concise expression.

  2. Devs will generally be surprised by [[DefineOwnProperty]] behavior, since it's not observably different from [[Put]] in current code except in some degenerate cases that aren't seen in non-adversarial scenarios.

Fair points so far, and why Dave Herman proposed using = and ; separation for a "fluent style" chaining extension:

obj.{ prop1 = val1; ... propN = valN; };

Dave's proposal (which I can't find atm, it may be in a gist) also supports method calling with obj as |this|, a la JQuery chaining style but without requiring the methods to all return |this|.

I would also assert that there's not a strong use-case for giving [[DefineOwnProperty]] substantially more concise syntax. (I'd agree with a somewhat terser API than the current Object.defineProperties(), but I don't think it needs actual syntax support.)

The data theft attack may mutate away from JSON and toward pure JS if mustache becomes popular enough under any syntax that uses [[Put]]. Yes, we could say "defensive coders must use Object.definePropert{y,ies}" but that won't have much effect.

When you do have a deep prototype chain, with details possibly buried deep in code that you don't want to look at, how often do you actually want to completely replace a given property?

Shadowing has its uses too. When you're making a work-alike but with AOP-ish advice, e.g. Various libraries support this kind of thing. But let's not ask what are essentially rhetorical questions. Here's what I think:

For new objects you create locally in a small-world codebase, : for define works as well as = for assign.

For the DOM, esp. the CSSOM, = is the only way.

I'm using = and : abstractly here, not pushing them as final syntax.

Therefore I believe that if we can't agree on = and : both having support in mustache, we should use different-enough looking syntax, a la Dave's misplaced gist for fluent style, and not get stuck here and end up with nothing.

# Allen Wirfs-Brock (12 years ago)

On May 29, 2012, at 10:43 AM, Axel Rauschmayer wrote:

Beware scenario-solving in an all-or-nothing way. There's no absolute good here, and while people may (due to Prototype's Object.extend) want = not : in most cases, that doesn't mean : is unnecessary.

Define properties manually when you really need it? I agree with Zakas et al.: it will be nearly impossible to teach. I have a hard time telling the two apart, too.

Another alternative: give up on mustache since it bends object literal syntax past its breaking point.

I see two options:

  1. Only do =. How will method definitions be done?
  2. Only do :, but with [[Put]].

Another alternative (or variation on 2) is to use a slightly more distinctive syntax for object extension literals. For example:

obj=.{ //"damp mustache"?? foo: bar //does a [[Put]] because it is inside =.{ };

But then you still loose the ability to define own data properties. There seem to be reasonable use cases that require them. For example, defining own properties within a constructor.

It seems that understanding the difference between defining a property and assigning to a property is a distinction that JS developers need to learn. Particularly, as object definition patterns migrate away from definition via assignment to more declarative forms (class definitions, object literals, class extension literals, etc.)

# Herby Vojčík (12 years ago)

Couldn't all this be solved by allowing = also for object literals and promoting its use? People get used to the fact that = is the "normal" way and : is the "special" way plus they are told JSON only has : for both historical and security reasons.

Because I feel the main problem felt here is "don't help developers do [[DefineProperty]] all over". I say : should definitelybe there (consistency, I value it very highly, so no = only), but by saying "using = is the right style" people can move away from using : in literal, so they will also not use it in mustache.

# Brendan Eich (12 years ago)

Allen Wirfs-Brock wrote:

It seems that understanding the difference between defining a property and assigning to a property is a distinction that JS developers need to learn. Particularly, as object definition patterns migrate away from definition via assignment to more declarative forms (class definitions, object literals, class extension literals, etc.)

Yet teachers do subset and can teach a subset that ignores definition and exposes only assignment.

I think based on explicit use of "teaching" and "new developers" verbiage that this is what we're hearing. If so, such a concern cannot constrain the entire non-subsetted language, any more than subsetting English means we all speak toddler-talk.

The refactoring concern is plausible in my view:

version 1: // my code is not too large and I make my own object with secrets in it. var obj = {pub: lic, sec: ret};

version 2: // evolved to split the construction in two: function makeBase(lic) { // do some logging or pre-processing/validation/normalization of lic here. return {pub: lic}; } var obj = makeBase(lic).{sec: ret};

version 3: // my code has grown, makeBase is popular, also virtualized, so discovered. var makeBase = appServices.findService("Base"); var obj = makeBase(lic).{sec: ret};

Now, because Murphy was an optimist, a bad actor can inject a different makeBase that defines a setter to steal 'sec''s value, ret.

Contrived? I think not. We have a combination here of a known exploit (JSON theft), a new operator that (under this example's hypothesis does what Tab wants, and assigns not defines), and codebase growth + generalization into reusable and discoverable parts that we've all seen.

Would using = instead of : really help, though? I could see the same exploit arising no matter what single character was used.

Would requiring Object.definePropert{y,ies} help? I think not, it simply won't occur to most developers who follow this arc to use that in time to be safe. And if hacked, they'll be outraged that so verbose an API-set must be used.

The only usable+secure extensions I see are two, so we don't confuse users with almost-identical syntax with quite different (when it matters most, when under attack) semantics:

A. obj.{prop: val, ...} as safe mustache, with : for define not assign.

B. obj.[prop = val; ...] with meta... of course, for fluent-style chained assignments of obj's props.

I use [ not { for the chained case so the bracketing as well as ;-separation (final ; is optional) and = not : distinguish B from A for readers looking at any punctuation.

# Brendan Eich (12 years ago)

Herby Vojčík wrote:

Couldn't all this be solved by allowing = also for object literals and promoting its use? People get used to the fact that = is the "normal" way and : is the "special" way plus they are told JSON only has : for both historical and security reasons.

Because I feel the main problem felt here is "don't help developers do [[DefineProperty]] all over". I say : should definitelybe there (consistency, I value it very highly, so no = only), but by saying "using = is the right style" people can move away from using : in literal, so they will also not use it in mustache.

Mark Miller and I advocated this in last week's meeting, but the counter-argument (Oliver, IIRC) was that people would use = in JSON by accident, not test their JSON, and we'd rerun the JSON theft exploit show.

I don't buy this either. People test or they deserve what they get. = not : in JSON? Never saw it, don't expect any JS support would cause it to arise. I could be wrong, but we're speculating here. No proofs.

Anyway, the more I think about it, the more I believe we should leave object literals alone, but consider my A/B from last post:

A. obj.{prop: val, ...} as safe mustache, with : for define not assign.

B. obj.[prop = val; ...] with meta... of course, for fluent-style chained assignments of obj's props.

Note that B could support method calls too, as Dave has proposed under curlier syntax.

# Allen Wirfs-Brock (12 years ago)

On May 29, 2012, at 11:17 AM, Herby Vojčík wrote:

Couldn't all this be solved by allowing = also for object literals and promoting its use? People get used to the fact that = is the "normal" way and : is the "special" way plus they are told JSON only has : for both historical and security reasons.

Because I feel the main problem felt here is "don't help developers do [[DefineProperty]] all over". I say : should definitelybe there (consistency, I value it very highly, so no = only), but by saying "using = is the right style" people can move away from using : in literal, so they will also not use it in mustache.

I actually considered including the above in my proposal precisely for consistency and the reasons that have been discussed on this thread. However, I ultimately decided against including it. Primarily because it further complicate the proposal and yet didn't seem to have compelling use cause = semantics in the new object (object lit) case.

I'm not necessarily opposed to it, if it would address these concerns.

# Mark S. Miller (12 years ago)

On Tue, May 29, 2012 at 11:30 AM, Allen Wirfs-Brock <allen at wirfs-brock.com>wrote:

On May 29, 2012, at 11:17 AM, Herby Vojčík wrote:

Couldn't all this be solved by allowing = also for object literals and promoting its use? People get used to the fact that = is the "normal" way and : is the "special" way plus they are told JSON only has : for both historical and security reasons.

Because I feel the main problem felt here is "don't help developers do [[DefineProperty]] all over". I say : should definitelybe there (consistency, I value it very highly, so no = only), but by saying "using = is the right style" people can move away from using : in literal, so they will also not use it in mustache.

I actually considered including the above in my proposal precisely for consistency and the reasons that have been discussed on this thread. However, I ultimately decided against including it. Primarily because it further complicate the proposal and yet didn't seem to have compelling use cause = semantics in the new object (object lit) case.

I'm not necessarily opposed to it, if it would address these concerns.

I don't share most of the concerns that have been raised in this thread. However, I find the argument from consistency compelling. I would like the object literal syntax to be the same as the apparent object literal syntax that appears in moustache. We should allow "=" either in both places or neither. I prefer both. But I mildly prefer neither over including it in just one of these.

# Herby Vojčík (12 years ago)

I sent the mail in attachment to the list, but it did not arrive yet, so I send it this way. It is exactly about making those two spec-wise the same thing. What do you think of it (since it DRYes it, it would not complicate the proposal and the resulting spec and language could have the consistency for this "hard-wired")?

# Erik Arvidsson (12 years ago)

On Tue, May 29, 2012 at 11:03 AM, Brendan Eich <brendan at mozilla.org> wrote:

Fair points so far, and why Dave Herman proposed using = and ; separation for a "fluent style" chaining extension:

obj.{    prop1 = val1;    ...    propN = valN;  };

Dave's proposal (which I can't find atm, it may be in a gist) also supports method calling with obj as |this|, a la JQuery chaining style but without requiring the methods to all return |this|.

code.google.com/p/traceur-compiler/source/browse/#git%2Ftest%2Ffeature%2FCascade

# Allen Wirfs-Brock (12 years ago)

I already have the specification semantics setup to work that way. It is really only the syntax rules that distinguishes the two forms.

# Tab Atkins Jr. (12 years ago)

On Tue, May 29, 2012 at 11:03 AM, Brendan Eich <brendan at mozilla.org> wrote:

Tab Atkins Jr. wrote:

I'm not making the assumption that "obj" in the examples is necessarily an Object with Object.prototype.  It's irrelevant to the two points I'm trying to make:

  1. The use-case for mustache is to replace current code that does a lot of property-assigning statements with a more concise expression.

  2. Devs will generally be surprised by [[DefineOwnProperty]] behavior, since it's not observably different from [[Put]] in current code except in some degenerate cases that aren't seen in non-adversarial scenarios.

Fair points so far, and why Dave Herman proposed using = and ; separation for a "fluent style" chaining extension:

obj.{    prop1 = val1;    ...    propN = valN;  };

Dave's proposal (which I can't find atm, it may be in a gist) also supports method calling with obj as |this|, a la JQuery chaining style but without requiring the methods to all return |this|.

I'm totally down with Dave's "weak 'with', only better" proposal. Dave's syntax made it look like a special kind of block with a restriction on what kind of statements were allowed. Because of this, it felt very natural to also have method-calling in there. Since this is really useful, this made me happy.

I'm also okay with it overlapping with the shadowing mustache. We already have the literal vs block syntax fight, and this doesn't seem to confuse authors (outside of funny talks about the return value of {}+[]...) - reproducing the same fight within mustache doesn't seem particularly bad, as long as things really do look and act like block vs literal.

I honestly find it slightly weird that the shadowing mustache is a mutator rather than returning a new object, but I wouldn't fight it. (In other words, I find the proto-of arrow a slightly more natural syntax to think of in this circumstance.)

# Nicholas C. Zakas (12 years ago)

On 5/29/2012 10:56 AM, Allen Wirfs-Brock wrote:

On May 29, 2012, at 10:08 AM, Tab Atkins Jr. wrote:

On Tue, May 29, 2012 at 9:45 AM, Mark S. Miller<erights at google.com> wrote:

Language design and extension is a complex process. Being supportive of multiple use cases generally makes a new feature more valuable and more likely to be adopted. It also makes for smaller, more consistent languages. Socially, support of multiple use cases helps a feature achieve consensus support. A "mustache" proposal that only allows "foo=bar" may not do enough to get the support behind it that is needed to make the cut. Neither might a proposal that lacks "foo=bar". The fact that there are multiple perspectives isn't bad, and it doesn't mean that we are "designing by committee". It is simply how language design works.

I'm pretty sure that we all understand that this is a complex process. I certainly appreciate all of the work that TC 39 puts into the process, and appreciate even more that there is transparency in the form of strawman proposals. Multiple perspectives are definitely good, and I'm just trying to provide another.

Finally, I'm skeptical of any self-styled representation of "normal developers" that isn't backed up with data. In particular, on this other proposals I generally receive "community" feedback that span all perspectives on the issues. (BTW, on the proposal in question, the positive feedback seems to be running quite a bit ahead of the negative). At least some of it seems to come from relatively "normal programmers" (although I have no idea how anybody would actually define that term). I'm familiar with the issues you raise, their general theme is something that we routinely discuss in TC39 (and really in any competent language design team). You really hould assume that we have considered and factored them into our proposals. It is perfectly fair, try to demonstrate why we made the wrong trade-offs in a proposal but this will seldom work if you only consider one dimension of the design. Try to be the most comprehensively reasoned voice, not the loudest.

As well you should be. What sort of data are you looking for? I'd be willing to bet that there wouldn't be a data set that would satisfy your parameters. I know that I'm speaking from 15 years of nonstop web development experience, at companies both small and large, and have worked with people of all skill levels. I definitely have not spent the past several years designing JavaScript or evolving it. And even though I am 100% sure that you tend to discuss a lot of issues in your meetings that we would care about, I don't think it serves anyone if we all assume that you all have already thought of everything and understand all of the issues.

Tab and I have, IMO, presented our points of view in a very calm, measured, rational way. That seems to be in stark contrast to a lot of the conversations that happen on this mailing list and others. I would hope that such feedback would be welcomed rather than eschewed.

# Nicholas C. Zakas (12 years ago)

On 5/29/2012 11:03 AM, Brendan Eich wrote:

Therefore I believe that if we can't agree on = and : both having support in mustache, we should use different-enough looking syntax, a la Dave's misplaced gist for fluent style, and not get stuck here and end up with nothing.

That was basically my point, that the object literal syntax will cause confusion. I really like Dave's syntax, which pretty much works the way that I would expect just from glancing at it.

# Brendan Eich (12 years ago)

Brendan Eich wrote:

The only usable+secure extensions I see are two, so we don't confuse users with almost-identical syntax with quite different (when it matters most, when under attack) semantics:

A. obj.{prop: val, ...} as safe mustache, with : for define not assign.

B. obj.[prop = val; ...] with meta... of course, for fluent-style chained assignments of obj's props.

I use [ not { for the chained case so the bracketing as well as ;-separation (final ; is optional) and = not : distinguish B from A for readers looking at any punctuation.

Stop me if you've heard this one before...

For B, instead of [] we could use () as the chained set/call brackets. E4X (ECMA-357) defines xml.(expr) as a filtering predicate expression, but no worries, E4X is going away.

Comments welcome on the idea of avoiding using { for something that is neither a start-object-literal (in B) nor a block (contents are quite constrained).

# Rick Waldron (12 years ago)

On Wednesday, May 30, 2012 at 12:09 AM, Brendan Eich wrote:

Brendan Eich wrote:

The only usable+secure extensions I see are two, so we don't confuse users with almost-identical syntax with quite different (when it matters most, when under attack) semantics:

A. obj.{prop: val, ...} as safe mustache, with : for define not assign.

B. obj.[prop = val; ...] with meta... of course, for fluent-style chained assignments of obj's props.

I use [ not { for the chained case so the bracketing as well as ;-separation (final ; is optional) and = not : distinguish B from A for readers looking at any punctuation.

Stop me if you've heard this one before...

For B, instead of [] we could use () as the chained set/call brackets. E4X (ECMA-357) defines xml.(expr) as a filtering predicate expression, but no worries, E4X is going away.

Comments welcome on the idea of avoiding using { for something that is neither a start-object-literal (in B) nor a block (contents are quite constrained).

Would property assignments be separated by comma or semicolon?

Eg.

o.( a = "alpha", b = "beta" )

v.

o.( a = "alpha"; b = "beta" )

# Brendan Eich (12 years ago)

Rick Waldron wrote:

On Wednesday, May 30, 2012 at 12:09 AM, Brendan Eich wrote:

Brendan Eich wrote:

The only usable+secure extensions I see are two, so we don't confuse users with almost-identical syntax with quite different (when it matters most, when under attack) semantics:

A. obj.{prop: val, ...} as safe mustache, with : for define not assign.

B. obj.[prop = val; ...] with meta... of course, for fluent-style chained assignments of obj's props.

I use [ not { for the chained case so the bracketing as well as ;-separation (final ; is optional) and = not : distinguish B from A for readers looking at any punctuation.

Stop me if you've heard this one before...

For B, instead of [] we could use () as the chained set/call brackets. E4X (ECMA-357) defines xml.(expr) as a filtering predicate expression, but no worries, E4X is going away.

Comments welcome on the idea of avoiding using { for something that is neither a start-object-literal (in B) nor a block (contents are quite constrained). Would property assignments be separated by comma or semicolon?

Eg.

o.( a = "alpha", b = "beta" )

v.

o.( a = "alpha"; b = "beta" )

Heh, I didn't say. Comma might be just as good meaning "no worse than semicolon" in light of object literals having different-enough brackets. Comma looks like the comma operator, and with () around it looks like an argument list, OTOH.

On the principle of making different semantics look different, I'd go with ; over , at this point. But perhaps there' s a more targeted argument one way or the other?

# Brendan Eich (12 years ago)

Brendan Eich wrote:

Would property assignments be separated by comma or semicolon?

Eg.

o.( a = "alpha", b = "beta" )

v.

o.( a = "alpha"; b = "beta" )

Heh, I didn't say. Comma might be just as good meaning "no worse than semicolon" in light of object literals having different-enough brackets. Comma looks like the comma operator, and with () around it looks like an argument list, OTOH.

On the principle of making different semantics look different, I'd go with ; over , at this point. But perhaps there' s a more targeted argument one way or the other?

A targeted homage: Smalltalk's fluent style used ; so we should too, inside () after a dot.

But it's not a usability argument, just a salute to one of the eight great programming languages.

# Gavin Barraclough (12 years ago)

On May 29, 2012, at 11:19 PM, Brendan Eich wrote:

o.( a = "alpha", b = "beta" )

v.

o.( a = "alpha"; b = "beta" )

The similarity between .{ foo: bar } & .{ foo= bar } concerns me, it's too footgunish. This I like.

# Mark S. Miller (12 years ago)

On Tue, May 29, 2012 at 11:19 PM, Brendan Eich <brendan at mozilla.org> wrote:

Brendan Eich wrote:

Would property assignments be separated by comma or semicolon?

Eg.

o.( a = "alpha", b = "beta" )

v.

o.( a = "alpha"; b = "beta" )

Heh, I didn't say. Comma might be just as good meaning "no worse than semicolon" in light of object literals having different-enough brackets. Comma looks like the comma operator, and with () around it looks like an argument list, OTOH.

On the principle of making different semantics look different, I'd go with ; over , at this point. But perhaps there' s a more targeted argument one way or the other?

A targeted homage: Smalltalk's fluent style used ; so we should too, inside () after a dot.

I appreciate this reason ;), but I like semicolon for another reason -- the parens suggest "factoring out" the "o.", as if

o.(stuff1; stuff2)

means the same thing as

o.stuff1; o.stuff2

where stuff1 and stuff2 are not identifiers, but (to take an extreme stance) anything that can appear within an expression statement between an initial "o." and a terminal ";".

Although it does not fall out of the factoring interpretation, I think it is still natural for the expression as a whole to have o as its value, since expression statements are not normally thought of as having a value.

However, I think it is just too much syntax to have both this and mustache. We should choose at most one. I would prefer neither to both.

But it's not a usability argument, just a salute to one of the eight great programming languages.

Ok, I gotta ask: Which are the other seven?

# T.J. Crowder (12 years ago)

On 30 May 2012 05:09, Brendan Eich <brendan at mozilla.org> wrote:

Brendan Eich wrote:

The only usable+secure extensions I see are two, so we don't confuse users with almost-identical syntax with quite different (when it matters most, when under attack) semantics:

A. obj.{prop: val, ...} as safe mustache, with : for define not assign.

B. obj.[prop = val; ...] with meta... of course, for fluent-style chained assignments of obj's props.

I use [ not { for the chained case so the bracketing as well as ;-separation (final ; is optional) and = not : distinguish B from A for readers looking at any punctuation.

Stop me if you've heard this one before...

For B, instead of [] we could use () as the chained set/call brackets. E4X (ECMA-357) defines xml.(expr) as a filtering predicate expression, but no worries, E4X is going away.

So A remains

obj.{x: val, y: val};

...where these are [[DefineOwnProperty]]

and B becomes

obj.(x = val; y = val);

...where these are [[Put]]

Is the idea to only have B, or to have both A and B? I see clear use cases for B. I'm not immediately seeing them for A but I came late-ish to the discussion.

-- T.J.

# François REMY (12 years ago)

If I correctly understood, A is for the supposedly existing case where someone would add a property to an object before you “extend.{}” it to alter the way that property is handled, in order to get acces to things you wouldn’t have thought they could gain access to but that you’re putting on a public field of an object you’re not sure to trust.

This is a very contreived use case, considering that if you can’t trust the object you’re working with, it can very possibly be a proxy, in which case even [[DefineOwnProperty]] can lead to unintended leaks.

From: T.J. Crowder Sent: Wednesday, May 30, 2012 11:17 AM To: Brendan Eich ; es-discuss at mozilla.org Subject: Re: New Object Extension Literal Strawman On 30 May 2012 05:09, Brendan Eich <brendan at mozilla.org> wrote:

Brendan Eich wrote:

The only usable+secure extensions I see are two, so we don't confuse users with almost-identical syntax with quite different (when it matters most, when under attack) semantics:

A. obj.{prop: val, ...} as safe mustache, with : for define not assign.

B. obj.[prop = val; ...] with meta... of course, for fluent-style chained assignments of obj's props.

I use [ not { for the chained case so the bracketing as well as ;-separation (final ; is optional) and = not : distinguish B from A for readers looking at any punctuation.

Stop me if you've heard this one before...

For B, instead of [] we could use () as the chained set/call brackets. E4X (ECMA-357) defines xml.(expr) as a filtering predicate expression, but no worries, E4X is going away.

So A remains

obj.{x: val, y: val};

...where these are [[DefineOwnProperty]]

and B becomes

obj.(x = val; y = val);

...where these are [[Put]]

Is the idea to only have B, or to have both A and B? I see clear use cases for B. I'm not immediately seeing them for A but I came late-ish to the discussion.

-- T.J.

# Herby Vojčík (12 years ago)

Brendan Eich wrote:

Brendan Eich wrote:

The only usable+secure extensions I see are two, so we don't confuse users with almost-identical syntax with quite different (when it matters most, when under attack) semantics:

A. obj.{prop: val, ...} as safe mustache, with : for define not assign.

B. obj.[prop = val; ...] with meta... of course, for fluent-style chained assignments of obj's props.

I use [ not { for the chained case so the bracketing as well as ;-separation (final ; is optional) and = not : distinguish B from A for readers looking at any punctuation.

Stop me if you've heard this one before...

For B, instead of [] we could use () as the chained set/call brackets. E4X (ECMA-357) defines xml.(expr) as a filtering predicate expression, but no worries, E4X is going away.

.{...} has established ways to do things, like .{"0":"foo", "1":"bar"} (and recently it even had .{[key]:value}).

I cannot see how would you do this in .(...)? .([0]="foo"; [1]="bar")? Or you introduce .["foo", "bar"]?

# Brendan Eich (12 years ago)

Herby Vojčík wrote:

.{...} has established ways to do things, like .{"0":"foo", "1":"bar"} (and recently it even had .{[key]:value}).

Established how? Mustache syntax was proposed last July, it's not fully in Harmony -- hence Allen's new strawman.

Computed property names in object literals was deferred at the last meeting. Arv argued convincingly that object literals should have static property names only. (This does not affect the @-prefix proposal, which is a strawman of sorts, although not written up by itself.)

I cannot see how would you do this in .(...)? .([0]="foo"; [1]="bar")? Or you introduce .["foo", "bar"]?

See later post suggesting obj.(foo = 1; bar = 2; baz()).

Non-Identifier property names would be a hard case, I think. You'd have to write things out the long way using obj['fo at o'), etc.

# Brendan Eich (12 years ago)

Brendan Eich wrote:

You'd have to write things out the long way using obj['fo at o'), etc.

Er, obj['fo at o'] = 1; obj['ba at r'] = 2; obj'ba at z', I mean.

# Herby Vojčík (12 years ago)

Brendan Eich wrote:

Herby Vojčík wrote:

.{...} has established ways to do things, like .{"0":"foo", "1":"bar"} (and recently it even had .{[key]:value}).

Established how? Mustache syntax was proposed last July, it's not fully in Harmony -- hence Allen's new strawman.

By basically saying "what is inside .{...} does same things as what is inside {...} and Allen said a few posts before that spec is already written that way ({...} is (new Object).{...}).

So, established as legacy from (extended) object literals.

I cannot see how would you do this in .(...)? .([0]="foo"; [1]="bar")? Or you introduce .["foo", "bar"]?

See later post suggesting obj.(foo = 1; bar = 2; baz()).

This does not answer the question, it's something else. I was asking on numberal (that is, probably, non-identifier from the subsequent parapgraph?) keys that can be included in (.){...} now.

# Allen Wirfs-Brock (12 years ago)

On May 30, 2012, at 6:16 AM, Brendan Eich wrote:

...

See later post suggesting obj.(foo = 1; bar = 2; baz()).

Non-Identifier property names would be a hard case, I think. You'd have to write things out the long way using obj['fo at o'), etc.

I don't think it is very hard:

MemberExpression : MemberExpression . ( CascadeList ) ...

CascadeList : Cascade CascadeList ; Cascade

Cascade : PropertyName = AssignmentExpression PropertyName Arguments PropertyName .( CascadeList )

where all the non-terminals not explicitly specified above have there current (future) ES5/6 definitions. In particular:

PropertyName : IdentifierName StringLiteral NumericLiteral @ Identifier //a hypothetical new addition for private name

I don't yet love using parens in this way, but it seems that it could work for all of these cascade forms

# Allen Wirfs-Brock (12 years ago)

On May 29, 2012, at 11:59 PM, Mark S. Miller wrote:

... I appreciate this reason ;), but I like semicolon for another reason -- the parens suggest "factoring out" the "o.", as if

o.(stuff1; stuff2)

means the same thing as

o.stuff1; o.stuff2

where stuff1 and stuff2 are not identifiers, but (to take an extreme stance) anything that can appear within an expression statement between an initial "o." and a terminal ";".

Although it does not fall out of the factoring interpretation, I think it is still natural for the expression as a whole to have o as its value, since expression statements are not normally thought of as having a value.

However, I think it is just too much syntax to have both this and mustache. We should choose at most one. I would prefer neither to both.

I think that

      o.{
           p1: expr0,
           p2() {return this.p1},
           get p3 () {someSideEffect(); return this.p1}
        };

and

      o.( 
           p1 = expr1;
           p2());
           p3   //I didn't include this case in the bnf I wrote up
       }

are syntacticly distinctive enough that there would be no reason to trade them off against each other. They should each be consider based upon their unique utility. An advantage of my recent strawman (and what we discussed at the TC39 meeting) is that it leveraged one construct for a larger number of use cases. That is generally a good think, if it doesn't create confusion. Unfortunately may strawman may not meet that mark.

If you are concerned about the overall syntactic complexity budget of the language, then we aren't limited to only trading the above two constructs off against each other. There are other new and relatively complex syntactic embellishments that we have adopted that IMO may not have as much general utility as either of the above. If we are concerned about the syntactic complexity budget, we should be looking globally at all the proposals. But, that is another conversation.

# Allen Wirfs-Brock (12 years ago)

Below added another rule to Cascade to allow get accessor properties to be accessed. This may not be all that important...

On May 30, 2012, at 11:02 AM, Allen Wirfs-Brock wrote:

On May 30, 2012, at 6:16 AM, Brendan Eich wrote:

...

See later post suggesting obj.(foo = 1; bar = 2; baz()).

Non-Identifier property names would be a hard case, I think. You'd have to write things out the long way using obj['fo at o'), etc.

I don't think it is very hard:

MemberExpression : MemberExpression . ( CascadeList ) ...

CascadeList : Cascade CascadeList ; Cascade

Cascade : PropertyName = AssignmentExpression PropertyName Arguments PropertyName .( CascadeList ) PropertyName

# Mark S. Miller (12 years ago)

On Wed, May 30, 2012 at 12:46 PM, Allen Wirfs-Brock <allen at wirfs-brock.com>wrote:

On May 29, 2012, at 11:59 PM, Mark S. Miller wrote:

... I appreciate this reason ;), but I like semicolon for another reason -- the parens suggest "factoring out" the "o.", as if

o.(stuff1; stuff2)

means the same thing as

o.stuff1; o.stuff2

where stuff1 and stuff2 are not identifiers, but (to take an extreme stance) anything that can appear within an expression statement between an initial "o." and a terminal ";".

Although it does not fall out of the factoring interpretation, I think it is still natural for the expression as a whole to have o as its value, since expression statements are not normally thought of as having a value.

However, I think it is just too much syntax to have both this and mustache. We should choose at most one. I would prefer neither to both.

I think that

      o.{
           p1: expr0,
           p2() {return this.p1},
           get p3 () {someSideEffect(); return this.p1}
        };

and

      o.(
           p1 = expr1;
           p2());
           p3   //I didn't include this case in the bnf I wrote up
       }

are syntacticly distinctive enough that there would be no reason to trade them off against each other. They should each be consider based upon their unique utility. An advantage of my recent strawman (and what we discussed at the TC39 meeting) is that it leveraged one construct for a larger number of use cases. That is generally a good think, if it doesn't create confusion. Unfortunately may strawman may not meet that mark.

If you are concerned about the overall syntactic complexity budget of the language, then we aren't limited to only trading the above two constructs off against each other. There are other new and relatively complex syntactic embellishments that we have adopted that IMO may not have as much general utility as either of the above. If we are concerned about the syntactic complexity budget, we should be looking globally at all the proposals. But, that is another conversation.

My concern is indeed the overall complexity budget. And I agree. I'm happy to have both of these if we can make some real complexity cuts elsewhere. I look forward to that other conversation ;).

# Rick Waldron (12 years ago)

On Wed, May 30, 2012 at 3:46 PM, Allen Wirfs-Brock <allen at wirfs-brock.com>wrote:

On May 29, 2012, at 11:59 PM, Mark S. Miller wrote:

... I appreciate this reason ;), but I like semicolon for another reason -- the parens suggest "factoring out" the "o.", as if

o.(stuff1; stuff2)

means the same thing as

o.stuff1; o.stuff2

where stuff1 and stuff2 are not identifiers, but (to take an extreme stance) anything that can appear within an expression statement between an initial "o." and a terminal ";".

Although it does not fall out of the factoring interpretation, I think it is still natural for the expression as a whole to have o as its value, since expression statements are not normally thought of as having a value.

However, I think it is just too much syntax to have both this and mustache. We should choose at most one. I would prefer neither to both.

I think that

      o.{
           p1: expr0,
           p2() {return this.p1},
           get p3 () {someSideEffect(); return this.p1}
        };

and

      o.(
           p1 = expr1;
           p2());
           p3   //I didn't include this case in the bnf I wrote up
       }

are syntacticly distinctive enough that there would be no reason to trade them off against each other. They should each be consider based upon their unique utility. An advantage of my recent strawman (and what we discussed at the TC39 meeting) is that it leveraged one construct for a larger number of use cases. That is generally a good think, if it doesn't create confusion. Unfortunately may strawman may not meet that mark.

Yes, I think the newer o.( foo = "thing"; bar = "other" ) is nice and will satisfy the need just the same.

I'm also 100% in agreement with Allen here. The paren form lacks the possibility of extending this syntax to existing forms, which IIRC we were all in agreement was one of the better and more convincing "wins" of the curly form - which lead to the agreeing that it was indeed worthy of it's own proposal.

This should not be read as opposition to the paren form, merely a circling of the cart.

# David Herman (12 years ago)

On May 29, 2012, at 9:33 AM, Tab Atkins Jr. wrote:

Same thoughts here. Regular devs (like me!) only see [[DefineOwnProperty]] when creating a literal, and then there's no observable distinction between these two in normal circumstances:

Bingo. The different between [[DefineOwnProperty]] and [[Put]] is subtle and rarely encountered when dealing with a new object.

The distinction between the two operations is a corner case of the language, and [[Put]] is the common case.

I don't think it's worth optimizing for the [[DefineOwnProperty]] case. We should just use the standard "foo:bar" syntax, and have it invoke [[Put]] behavior.

I agree we should only be considering syntax for [[Put]] -- syntactic sugar should be to support common patterns, not to invent patterns that don't fit smoothly within the rest of the language.

However, I really, really oppose reusing syntax that's used for declarative data construction (i.e., literals) to notate update operations. We should not use the object literal notation for [[Put]]. We should use assignment syntax, which corresponds to doing assignment on an object.

This was the motivation between my blog post a while back on a different syntax for mustache:

http://blog.mozilla.org/dherman/2011/12/01/now-thats-a-nice-stache/

tl;dr: instead of an object literal, the RHS of mustache should have a syntax that corresponds more closely to that of blocks. It allows assignments and method calls.

array.{
    pop();
    pop();
    pop();
};

path.{
    moveTo(10, 10);
    stroke("red");
    fill("blue");
    ellipse(50, 50);
};

this.{
    foo = 17;
    bar = "hello";
    baz = true;
};

(I'll eventually write up a new strawman for it, but I'm working on the new wiki right now which I consider higher priority.)

# David Herman (12 years ago)

On May 29, 2012, at 9:59 AM, Nicholas C. Zakas wrote:

var element = document.getElementById("myDiv"); element.{ innerHTML: "Hello world!", className: "enabled" };

Great example. With my proposed operator, it would instead be:

var element = document.getElementById("myDiv"); element.{ innerHTML = "Hello world!"; className = "enabled"; }

Looks like the corresponding long-hand, and does the same thing as the corresponding long-hand.

# Rick Waldron (12 years ago)

On Wed, May 30, 2012 at 5:22 PM, David Herman <dherman at mozilla.com> wrote:

On May 29, 2012, at 9:33 AM, Tab Atkins Jr. wrote:

Same thoughts here. Regular devs (like me!) only see [[DefineOwnProperty]] when creating a literal, and then there's no observable distinction between these two in normal circumstances:

Bingo. The different between [[DefineOwnProperty]] and [[Put]] is subtle and rarely encountered when dealing with a new object.

The distinction between the two operations is a corner case of the language, and [[Put]] is the common case.

I don't think it's worth optimizing for the [[DefineOwnProperty]] case. We should just use the standard "foo:bar" syntax, and have it invoke [[Put]] behavior.

I agree we should only be considering syntax for [[Put]] -- syntactic sugar should be to support common patterns, not to invent patterns that don't fit smoothly within the rest of the language.

However, I really, really oppose reusing syntax that's used for declarative data construction (i.e., literals) to notate update operations. We should not use the object literal notation for [[Put]]. We should use assignment syntax, which corresponds to doing assignment on an object.

This was the motivation between my blog post a while back on a different syntax for mustache:

blog.mozilla.org/dherman/2011/12/01/now-thats-a-nice-stache

tl;dr: instead of an object literal, the RHS of mustache should have a syntax that corresponds more closely to that of blocks. It allows assignments and method calls.

array.{ pop(); pop(); pop(); };

path.{ moveTo(10, 10); stroke("red"); fill("blue"); ellipse(50, 50); };

this.{ foo = 17; bar = "hello"; baz = true; };

This is beautiful and looks powerful -- will it still meet the most common/obvious use case?

var div = document.createElement().

div.{ style.{ color = "red"; left = "100px"; } };

(the nest is the key)

# David Herman (12 years ago)

On May 29, 2012, at 10:08 AM, Tab Atkins Jr. wrote:

Given that these two are identical to most devs, giving them distinct syntaxes in the strawman is problematic. Further, and much more importantly, [[DefineOwnProperty]] is nearly never the behavior that a dev will actually want. Devs are not going to be replacing all their Object.defineProperties() calls with monocle-mustache, because no one actually uses that function in normal production, so there's nothing to replace. Normal devs do a lot of property assigning, though, and that's the use-case we're targetting with this.

Yes yes yes.

If it's desperately important that we don't make "obj.{foo:bar}" do a [[Put]], then it would be much better to simply not allow that syntax at all. Make monocle-mustache solely use the "obj.{foo=bar}" syntax, so that the other one is a syntax error.

Yes again. I don't see enough evidence that DefineOwnProperty is important enough of an operation to warrant dedicated syntax. Syntax should be reserved for the absolute most common operations of the language. Corner case operations should remain as API's. If Object.defineProperties is too cumbersome (and it is), we should standardize nicer API's for it.

There's no good reason to give devs the other type of functionality besides completeness, and using it will almost always be a mistake on the dev's part, I think.

Yes. (Did I mention yes?) Completeness is an anti-goal for syntax.

# David Herman (12 years ago)

On May 30, 2012, at 2:27 PM, Rick Waldron wrote:

On Wed, May 30, 2012 at 5:22 PM, David Herman <dherman at mozilla.com> wrote:

array.{ pop(); pop(); pop(); };

path.{ moveTo(10, 10); stroke("red"); fill("blue"); ellipse(50, 50); };

this.{ foo = 17; bar = "hello"; baz = true; };

This is beautiful and looks powerful -- will it still meet the most common/obvious use case?

var div = document.createElement().

div.{ style.{ color = "red"; left = "100px"; } };

I didn't specify in my blog post. :) I'm open to it. I tend to find the nested syntax a little brain-exploding, but I understand the motivation.

# Rick Waldron (12 years ago)

On Wed, May 30, 2012 at 5:22 PM, David Herman <dherman at mozilla.com> wrote:

On May 29, 2012, at 9:33 AM, Tab Atkins Jr. wrote:

Same thoughts here. Regular devs (like me!) only see [[DefineOwnProperty]] when creating a literal, and then there's no observable distinction between these two in normal circumstances:

Bingo. The different between [[DefineOwnProperty]] and [[Put]] is subtle and rarely encountered when dealing with a new object.

The distinction between the two operations is a corner case of the language, and [[Put]] is the common case.

I don't think it's worth optimizing for the [[DefineOwnProperty]] case. We should just use the standard "foo:bar" syntax, and have it invoke [[Put]] behavior.

I agree we should only be considering syntax for [[Put]] -- syntactic sugar should be to support common patterns, not to invent patterns that don't fit smoothly within the rest of the language.

However, I really, really oppose reusing syntax that's used for declarative data construction (i.e., literals) to notate update operations. We should not use the object literal notation for [[Put]]. We should use assignment syntax, which corresponds to doing assignment on an object.

This was the motivation between my blog post a while back on a different syntax for mustache:

blog.mozilla.org/dherman/2011/12/01/now-thats-a-nice-stache

tl;dr: instead of an object literal, the RHS of mustache should have a syntax that corresponds more closely to that of blocks. It allows assignments and method calls.

array.{ pop(); pop(); pop(); };

path.{ moveTo(10, 10); stroke("red"); fill("blue"); ellipse(50, 50); };

this.{ foo = 17; bar = "hello"; baz = true; };

In case anyone is interested, this syntax can be test driven using Traceur, exactly as shown above:

Take a look at these screen shots:

Creating a div with innerHTML: gyazo.com/50840d8fa61284af45817c0884094952.png

Multiple pop()s from an array: gyazo.com/53032b7d0c39e31cda727c579ceb4059.png

traceur-compiler.googlecode.com/git/demo/repl.html

Very useful for getting hands dirty and actually "trying before you buy"

# Brendan Eich (12 years ago)

Rick Waldron wrote:

In case anyone is interested, this syntax can be test driven using Traceur, exactly as shown above:

Take a look at these screen shots:

Creating a div with innerHTML: gyazo.com/50840d8fa61284af45817c0884094952.png

Multiple pop()s from an array: gyazo.com/53032b7d0c39e31cda727c579ceb4059.png

traceur-compiler.googlecode.com/git/demo/repl.html

Very useful for getting hands dirty and actually "trying before you buy"

Thanks for pointing this out (Arv did too, yesterday). +1 to Arv for implementing in Traceur.

# David Herman (12 years ago)

On May 29, 2012, at 10:56 AM, Allen Wirfs-Brock wrote:

On May 29, 2012, at 10:08 AM, Tab Atkins Jr. wrote:

If it's desperately important that we don't make "obj.{foo:bar}" do a [[Put]], then it would be much better to simply not allow that syntax at all. ...

Language design and extension is a complex process. Being supportive of multiple use cases generally makes a new feature more valuable and more likely to be adopted.

Let's not be dismissive -- cutting features is not the same as design-by-scenario-solving. I agree with Tab that DefineOwnProperty is not important enough to warrant syntax, and we can still end up with a feature that is quite general-purpose and composable.

I'm actually a little surprised that you are in favor of [[DefineOwnProperty]] rather than [[Put]], since you often point out that most of the time code should be oblivious to the details of the prototype chain.

Finally, I'm skeptical of any self-styled representation of "normal developers" that isn't backed up with data.

That's too tall an order. None of us has that kind of data. All we can do in most cases is speak from experience. (Not to say empirical research wouldn't be useful, but none of us really has the resources to do that kind of science.) Meanwhile, it's not a stretch to say that normal development in JS does not pay attention to or use [[DefineOwnProperty]]. It wasn't even surfaced in API's until the very recent ES5!

# Herby Vojčík (12 years ago)

You need (need in the sense "having readable construct to perform") .{...} with [[DefineOwnProperty]] not for "foo":"bar" (yes, there [[Put]] should happen), but for dynamically adding concise methods and/or getters/setters to objects.

Using API is much more cumbersome here. And lots of bugs can appear if people will use .defineProperties instead of .defineMethod, for example. And object literal extension, because it has identical syntax, is super readable.

Not that I don't like the idea, by the way... but stopping literal extension is too much. Retain it in some way (like foo.={literal} or so).

# Brendan Eich (12 years ago)

Herby Vojčík wrote:

You need (need in the sense "having readable construct to perform") .{...} with [[DefineOwnProperty]] not for "foo":"bar" (yes, there [[Put]] should happen

No, again: JSON theft attack possible again with [[Put]]. Declarative syntax is foo: bar. That means define not assign.

), but for dynamically adding concise methods and/or getters/setters to objects.

These need define, of course, but it's not correct to say that data properties can use assign under :-separated object-literal(-based) syntax.

# Herby Vojčík (12 years ago)

Brendan Eich wrote:

Herby Vojčík wrote:

You need (need in the sense "having readable construct to perform") .{...} with [[DefineOwnProperty]] not for "foo":"bar" (yes, there [[Put]] should happen

No, again: JSON theft attack possible again with [[Put]]. Declarative syntax is foo: bar. That means define not assign.

Sorry, but I never said that. Yes, I wrote it too dense, but I did not meant that, I meant "foo":"bar" is not the reason for which it was needed, there [[Put]] is desired bejaviour and .(foo="bar") is the right one to choose.

I never said .{"foo":"bar"} should do [[Put]].

), but for dynamically adding concise methods and/or getters/setters to objects.

These need define, of course, but it's not correct to say that data properties can use assign under :-separated object-literal(-based) syntax.

See above.

# Brendan Eich (12 years ago)

Herby Vojčík wrote:

Brendan Eich wrote:

Herby Vojčík wrote:

You need (need in the sense "having readable construct to perform") .{...} with [[DefineOwnProperty]] not for "foo":"bar" (yes, there [[Put]] should happen

No, again: JSON theft attack possible again with [[Put]]. Declarative syntax is foo: bar. That means define not assign.

Sorry, but I never said that. Yes, I wrote it too dense, but I did not meant that, I meant "foo":"bar" is not the reason for which it was needed, there [[Put]] is desired bejaviour and .(foo="bar") is the right one to choose.

Ok, whew! Thanks for clarifying.

# David Herman (12 years ago)

On May 30, 2012, at 2:48 PM, Herby Vojčík wrote:

You need (need in the sense "having readable construct to perform") .{...} with [[DefineOwnProperty]] not for "foo":"bar" (yes, there [[Put]] should happen), but for dynamically adding concise methods and/or getters/setters to objects.

Using API is much more cumbersome here. And lots of bugs can appear if people will use .defineProperties instead of .defineMethod, for example. And object literal extension, because it has identical syntax, is super readable.

That makes sense, as far as that goes.

Not that I don't like the idea, by the way... but stopping literal extension is too much. Retain it in some way (like foo.={literal} or so).

Yeah, it's only one character, but it makes a world of difference IMO. The importance is that -- I claim -- the ":" character in a literal does not intuitively mean "do this imperative operation." Instead, the whole literal reads as "this is a bunch of data, give me an object with that data in it." Put differently, it reads as an atomic operation (even though technically it has an operational definition).

That said, I'm still not at all convinced that the use cases of defining getters/setters on existing objects really needs new syntax. We have syntax for getters/setters on new objects. We (and/or devs) can define more usable API's for adding getters/setters to existing objects. I don't see that it's a common enough use case to be worth another syntactic addition.

# Brendan Eich (12 years ago)

Mark S. Miller wrote:

I think that

          o.{
               p1: expr0,
               p2() {return this.p1},
               get p3 () {someSideEffect(); return this.p1}
            };

and

          o.(
               p1 = expr1;
               p2());
               p3   //I didn't include this case in the bnf I wrote up
           }

are syntacticly distinctive enough that there would be no reason
to trade them off against each other.  They should each be
consider based upon their unique utility.  An advantage of  my
recent strawman (and what we discussed at the TC39 meeting) is
that it leveraged one construct for  a larger number of use cases.
 That is generally a good think, if it doesn't create confusion.
 Unfortunately may strawman may not meet that mark.

If you are concerned about the overall syntactic complexity budget
of the language, then we aren't limited to only trading the above
two constructs off against each other. There are other new and
relatively complex syntactic embellishments that we have adopted
that IMO may not have as much general utility as either of the
above.  If we are concerned about the  syntactic complexity
budget, we should be looking globally at all the proposals.  But,
that is another conversation.

My concern is indeed the overall complexity budget. And I agree. I'm happy to have both of these if we can make some real complexity cuts elsewhere. I look forward to that other conversation ;).

I think this is too blind of the particulars. We shouldn't bean-count when complexity-budgeting because we can get stuck hill-climbing at a local maximum where there's a better hill to climb just over the horizon. My TXJS talk last year went on about this a bit too much but it is a recurring theme.

I would say we should cater to [[Put]] common cases with custom syntax first, and wait for extend-with-accessors use-cases to feel some pain from having to use the ES5 Object APIs. So I agree with Nicholas, Tab, and Dave.

Further, you and I agree syntax should distinguish distinct-enough semantics, so no : for [[Put]], rather =. We both like the Smalltalk homage. Dave reminded me of how the {}-bracketed form looks nicer than the () form, though, because it looks like a (restricted as to parse tree children) block.

I'm ok with either, but that means I must come down against mustache as proposed -- obj.{prop: val, get x()..., m() {...}}. I don't see the define instead of assign use-case as deserving this syntax, and now that Dave has recapitulated it, the imperative update hiding in declarative syntax rubs me the wrong way too.

# Herby Vojčík (12 years ago)

David Herman wrote:

On May 30, 2012, at 2:48 PM, Herby Vojčík wrote:

You need (need in the sense "having readable construct to perform") .{...} with [[DefineOwnProperty]] not for "foo":"bar" (yes, there [[Put]] should happen), but for dynamically adding concise methods and/or getters/setters to objects.

Using API is much more cumbersome here. And lots of bugs can appear if people will use .defineProperties instead of .defineMethod, for example. And object literal extension, because it has identical syntax, is super readable.

That makes sense, as far as that goes.

Not that I don't like the idea, by the way... but stopping literal extension is too much. Retain it in some way (like foo.={literal} or so).

Yeah, it's only one character, but it makes a world of difference IMO. The importance is that -- I claim -- the ":" character in a literal does not intuitively mean "do this imperative operation." Instead, the whole literal reads as "this is a bunch of data, give me an object with that data in it." Put differently, it reads as an atomic operation (even though technically it has an operational definition).

I can read literal extension in the same way: "this is a bunch of data, make the receiver object contain that data in it".

That said, I'm still not at all convinced that the use cases of defining getters/setters on existing objects really needs new syntax. We have syntax for getters/setters on new objects. We (and/or devs) can define more usable API's for adding getters/setters to existing objects. I don't see that it's a common enough use case to be worth another syntactic addition.

I was mostly concerned about concise methods. Getters/setters are a nice addition, it's more readable to

foo.={ get x() { return thex; } };

than

Object.defineProperty(foo, "x", {get: () => thex, configurable: true});

# David Herman (12 years ago)

On May 30, 2012, at 3:16 PM, Herby Vojčík wrote:

I can read literal extension in the same way: "this is a bunch of data, make the receiver object contain that data in it".

OK, but that agrees with my point, which is that the ":" doesn't carry an operational connotation. To be specific, if the syntax were:

obj.={ x: 1, y: 2 }

then the way you'd interpret it is "here's a bunch of (declaratively specified) data, please add it all atomically to object," and it's the "=" that conveys the update action.

To be clear, my position is:

obj.{ x: 1, y: 2 }   // over my dead body
obj.={ x: 1, y: 2 }  // as [[DefineOwnProperty]], OK, but I'm not sure it's worth it
obj.{ x = 1; y = 2 } // as [[Put]], I'm cool with it

I was mostly concerned about concise methods.

For most cases, you just want [[Put]]. And I favor thin arrow for concise standalone methods/constructors.

# Axel Rauschmayer (12 years ago)

I was mostly concerned about concise methods.

For most cases, you just want [[Put]]. And I favor thin arrow for concise standalone methods/constructors.

How about the following use case? Adding a method with a super-reference to an existing object.

# Allen Wirfs-Brock (12 years ago)

On May 30, 2012, at 2:17 AM, T.J. Crowder wrote:

On 30 May 2012 05:09, Brendan Eich <brendan at mozilla.org> wrote: ... So A remains

obj.{x: val, y: val};

...where these are [[DefineOwnProperty]]

and B becomes

obj.(x = val; y = val);

...where these are [[Put]]

Is the idea to only have B, or to have both A and B? I see clear use cases for B. I'm not immediately seeing them for A but I came late-ish to the discussion.

Mustache (A) is partially about more concise expression of object abstraction patterns, particularly WRT method properties.

Consider the following exemplar that is typical of code you might see today for defining a class like abstraction:

//Define a simple Point abstraction over objects function Point (x,y) { this.x = x; this.y = y; }

Point.prototype = function plus(aPoint) { return new Point(this.x+aPoint.x,this.y+aPoint.y };

Point.prototype = function distanceOrigin() { return Math.sqrt(this.xthis.x+this.ythis.y) };

Using mustache you could say:

//Define a simple Point abstraction over objects function Point (x,y) { this.{x, y}; }

Point.prototype.{ plus(aPoint) { return new Point(this.x+aPoint.x,this.y+aPoint.y)}, distanceOrigin() {return Math.sqrt(this.xthis.x+this.ythis.y)} };

This is the just the basic Es1-3 "class" pattern made more concise and arguably more readable (once you learn the syntax). To me, it is a more direct expression of the programmer's intent.

But there is more to concise methods with Mustache than just conciseness of expression. There is an important semantic distinction.

The methods defined in in the mustache example are non-enumerable properties, just like the methods defined in a class declaration and the methods of the built-in objects. This is almost surely what you want for methods that you are defining on a prototype, yet with ES5 you have you use Object.defineProperty to accomplish it and prior to ES5 it wasn't possible at all.

Most importantly, mustache provide a syntactic context for binding |super| references in methods:

Image I need to over-ride a prototype method on a per instance basis. For example:

let p100 = new Point(50,50); p100.{ distance(aPoint) {return Math.max(super(aPoint), 100)} //max distance is 100 };

This correctly binds the per instance "distance" method so the |super| reference works correctly. The alternative using = : p100.distance = function (aPoint) {return Math.max(super.distance(aPoint), 100)} would not work correctly and would produce either a static or dynamic error on the super reference.

In the super proposal harmony:object_initialiser_super#imperatively_binding_super , installing such a method onto an object while maintaining a correct |super| binding required use of Object.defineMethod. It's been argued, and I agree, that defineMethod in this context would be too error prone. Mustache eliminates the need for defineMethod for these most common cases.

You might argue that |super| only makes sense within a class. However, the above example could easily be rewritten using class syntax:

class Point { plus(aPoint) { return new Point(this.x+aPoint.x,this.y+aPoint.y} distanceOrigin() {return Math.sqrt(this.xthis.x+this.ythis.y)} constructor(x,y) {this.{x,y}} }; let p100 = new Point(50,50); p100.{ distance(aPoint) {return Math.max(super(aPoint), 100)} //max distance is 100 };

Using a class to define the abstraction doesn't change the fact that there are scenarios where it is useful to dynamically add per instance methods that augment (via super calls) prototype behavior.

Basically, mustache should be seen as the normal way to define methods on pre-existing objects. Its more concise, correctly makes methods non-enumerable, and support super references.

# David Herman (12 years ago)

On May 30, 2012, at 4:00 PM, Axel Rauschmayer wrote:

I was mostly concerned about concise methods.

For most cases, you just want [[Put]]. And I favor thin arrow for concise standalone methods/constructors.

How about the following use case? Adding a method with a super-reference to an existing object.

An API sounds totally appropriate to me.

Remember, we're not talking about "should this be possible." We're talking about "is this so incredibly common that it deserves new syntax."

# Brendan Eich (12 years ago)

At last week's meeting we deferred super outside of class methods.

# Allen Wirfs-Brock (12 years ago)

On May 30, 2012, at 4:15 PM, Brendan Eich wrote:

At last week's meeting we deferred super outside of class methods.

We talked about it, but I don't see the above statement captured in the minutes. It also isn't clear what you mean by "deferred". In a previous message you said that @privateName was deferred which I interpret as meaning deferred until another meeting. We certainly need to talk about a way to define private keyed methods in class declarations and @name seems like the most likely alternative. Similar, I think it is reasonable to say we deferred making a decision to have super outside of classes. That doesn't mean that there still isn't interest in doing so and that some of us want to consider talking about how to do so.

Also, last week there was support for .( expressed both from the perspective of the Smalltalk-like cascaded expression use cases and from the definitional object extension perspective. My perception is that different people were had primary interests in the different use cases. Just because we may be on the path of a solution to the cascaded expression use cases doesn't mean there isn't still interest and support for the definitional use cases.

# Anton Kovalyov (12 years ago)

I have a question. Here (gist.github.com/9a6d60e9c15ac4239b3d) I took a piece of existing boilerplate code and rewrote it using the new syntax. However on L18 I have data-disqus-id that cannot be used on a left hand side of assignment. What's a preferred way to deal with this? Move it to the outside?

Thanks, Anton

# Tab Atkins Jr. (12 years ago)

On Wed, May 30, 2012 at 4:34 PM, Anton Kovalyov <ml at kovalyov.net> wrote:

I have a question. Here (gist.github.com/9a6d60e9c15ac4239b3d) I took a piece of existing boilerplate code and rewrote it using the new syntax. However on L18 I have data-disqus-id that cannot be used on a left hand side of assignment. What's a preferred way to deal with this? Move it to the outside?

Two possibiliities.

If you use the dataset API, like you should, there's no problem:

widget.{ ... dataset.disqusId = "dsq1"; };

Alternately, you can keep using setAttribute, since Dave's syntax allows method calls as well:

widget.{ ... setAttribute('data-disqus-id', "dsq1"); }

# Brendan Eich (12 years ago)

Allen Wirfs-Brock wrote:

On May 30, 2012, at 4:15 PM, Brendan Eich wrote:

At last week's meeting we deferred super outside of class methods.

We talked about it, but I don't see the above statement captured in the minutes.

From Rick's notes of day 2:

AWB:

  • Asserts that super is defined correctly for classes (explanation)

Should super be allowed in all function forms or restricted to classes?

Resolution: Defer super outside of classes

See esdiscuss/2012-May/022838

It also isn't clear what you mean by "deferred".

One precedent:

strawman:deferred

We sometimes kill things outright too.

What we agreed to last week: no |super| outside of classes in ES6, no Object.defineMethod in ES6. That's what I recall and what the notes say (re: |super|).

In a previous message you said that @privateName was deferred which I interpret as meaning deferred until another meeting.

Missed ES6 but we have to consider misses that are errors to correct, yes. Sorry for using deferred two different ways -- |super| outside of classes was not in this category.

We certainly need to talk about a way to define private keyed methods in class declarations and @name seems like the most likely alternative. Similar, I think it is reasonable to say we deferred making a decision to have super outside of classes.

No, not from what I saw and what Rick recorded in the notes he took.

That doesn't mean that there still isn't interest in doing so and that some of us want to consider talking about how to do so.

Sure,

Also, last week there was support for .( expressed both from the perspective of the Smalltalk-like cascaded expression use cases and from the definitional object extension perspective.

Those are two different use-cases, operational semantics, and (therefore) syntaxes, though -- this thread makes that pretty clear.

My perception is that different people were had primary interests in the different use cases. Just because we may be on the path of a solution to the cascaded expression use cases doesn't mean there isn't still interest and support for the definitional use cases.

Who said there isn't "interest"? We're trying to keep what's in ES6 consistent, which is why I used "deferred". I'd be happy deferring beyond strawman at this point. We need to button down ES6.

People can pursue many interests and we do Harmony work beyond ES6 even in the meetings, but the decision last week was not to put |super| outside of classes into ES6, and therefore not to put Object.defineMethod into ES6. I recall this clearly and Rick's notes have at least the first bit.

# Allen Wirfs-Brock (12 years ago)

On May 30, 2012, at 3:26 PM, David Herman wrote:

To be clear, my position is:

obj.{ x: 1, y: 2 } // over my dead body obj.={ x: 1, y: 2 } // as [[DefineOwnProperty]], OK, but I'm not sure it's worth it obj.{ x = 1; y = 2 } // as [[Put]], I'm cool with it

Well, I don't think suicide threats are a very good way to reach consensus, but to balance things out I'll enter my "dead body" threat against the last form ;-)

I now could be quite happy with the paren based cascade idea that Brendan suggested last night. I'm surprised that no one has commented on the bnf I provided in a subsequent message.

{ /* stuff */ } already has too many different alternative forms of /stuff/ in ES. We don't need another different variant with different rules. The paren-based syntax seems to be a good way to avoid that issue and is perhaps even suggestive that we are dealing with a list of abbreviated expressions

On the other hand, using the exact same syntax for to define (rather than updating) properties (including methods) of a new singleton object and to define additional properties (including methods) for a pre-existing singleton object seems conceptually clean and economical of syntax.

I see nothing at all wrong and much to like about:

let obj = { a: v1, b: v2, m1 (a,x) {...}, get g1 {} {...} }

if (pred) obj.{ c: v3, m2() {...}, m3() {...} } else obj.{ d: v4, m4() {...}, m5() {...} } }

Saying obj.={ is plausible although I don't think the = adds anything useful.

# Rick Waldron (12 years ago)

On Wednesday, May 30, 2012 at 7:40 PM, Tab Atkins Jr. wrote:

On Wed, May 30, 2012 at 4:34 PM, Anton Kovalyov <ml at kovalyov.net> wrote:

I have a question. Here (gist.github.com/9a6d60e9c15ac4239b3d) I took a piece of existing boilerplate code and rewrote it using the new syntax. However on L18 I have data-disqus-id that cannot be used on a left hand side of assignment. What's a preferred way to deal with this? Move it to the outside?

Two possibiliities.

If you use the dataset API, like you should, there's no problem:

widget.{ ... dataset.disqusId = "dsq1"; };

Maybe Dave can clarify, but I think it would actually be:

widget.{ dataset.{ disqusId = "..."; } };

The root issue is conflation of DOM properties and HTML element attributes (which haunts dataset...)

# Tab Atkins Jr. (12 years ago)

On Wed, May 30, 2012 at 4:54 PM, Rick Waldron <waldron.rick at gmail.com> wrote:

On Wednesday, May 30, 2012 at 7:40 PM, Tab Atkins Jr. wrote:

If you use the dataset API, like you should, there's no problem:

widget.{ ... dataset.disqusId = "dsq1"; };

Maybe Dave can clarify, but I think it would actually be:

widget.{   dataset.{     disqusId = "...";   } };

That's possible, sure, but not necessary. My form is valid as well, and easier if you're only setting one property.

# Anton Kovalyov (12 years ago)

Thanks. I've updated my gist. I personally think that Rick's suggestion works better in this case because it makes the whole tree of properties more visible. But, at this point, it's just a styling issue.

Anton

# Brendan Eich (12 years ago)

Tab Atkins Jr. wrote:

On Wed, May 30, 2012 at 4:54 PM, Rick Waldron<waldron.rick at gmail.com> wrote:

On Wednesday, May 30, 2012 at 7:40 PM, Tab Atkins Jr. wrote:

If you use the dataset API, like you should, there's no problem:

widget.{ ... dataset.disqusId = "dsq1"; }; Maybe Dave can clarify, but I think it would actually be:

widget.{ dataset.{ disqusId = "..."; } };

That's possible, sure, but not necessary. My form is valid as well, and easier if you're only setting one property.

Right -- people sometimes write obj.{foo: bar} (mustache) or equivalent (obj.(foo = bar) or obj.{foo = bar}) but these are poor style and in an expression context, obj.foo = bar (for the [[Put]] forms only) should be used instead.

Given this simplification at the outermost or outside-the-nest sense, we could support what you wrote: widget.{ ... dataset.disqusId = "dsq1"; ... } deeper in the nest. I agree we should.

# Brendan Eich (12 years ago)

Allen Wirfs-Brock wrote:

I now could be quite happy with the paren based cascade idea that Brendan suggested last night. I'm surprised that no one has commented on the bnf I provided in a subsequent message.

Grammar is a detail at this point. The objection to parens is reasonable enough: they look bad when split across multiple lines:

obj.( deep.( deeper: val; deeper2: val2 ) )

(assume everything doesn't fit on one line).

Schemers might style this thus:

obj.(deep.(deeper: val; deeper2: val2))

but that looks bad too -- too ragged-left with arbitrary indentation and )))) pile-ups at the end.

Braces look right because they follow K&R style, which as Crock points out wins in JS because of restricted productions, specifically

return { obj: literal, here: right };

So,

obj.{ deep.{ deeper: val; deeper2: val2 } }

# Allen Wirfs-Brock (12 years ago)

On May 30, 2012, at 4:42 PM, Brendan Eich wrote:

Allen Wirfs-Brock wrote:

On May 30, 2012, at 4:15 PM, Brendan Eich wrote:

At last week's meeting we deferred super outside of class methods.

We talked about it, but I don't see the above statement captured in the minutes.

From Rick's notes of day 2:

AWB:

  • Asserts that super is defined correctly for classes (explanation)

Should super be allowed in all function forms or restricted to classes?

Resolution: Defer super outside of classes

oops, I searched the next message where you had added some additions to Rick's notes. I didn't notice that it wasn't a complete quote of the original message...

See esdiscuss/2012-May/022838

It also isn't clear what you mean by "deferred".

One precedent:

strawman:deferred

We sometimes kill things outright too.

What we agreed to last week: no |super| outside of classes in ES6, no Object.defineMethod in ES6. That's what I recall and what the notes say (re: |super|).

I also don't see the mention of defineMethod although I know we talked about it.

I agree that we have a deferral of super/defineMethod but I personally don't think the former is going to stay deferred, if we reach agreement on classes that have super.

Ultimately, I think we will find that we can't have a semantics like |super| that is only usable within a syntactic class definition but has no reflective support and no way way to programmatically desugar a class definition into the exact equivalent set of object definitions. That's only half a solution. support of |super| outside of class is the only think missing to enable that. So, it something I will likely continue top push on.

In that light mustache with super containing concise methods seems like a very good way to avoid the defineMethod usability issues.

...

Also, last week there was support for .( expressed both from the perspective of the Smalltalk-like cascaded expression use cases and from the definitional object extension perspective.

Those are two different use-cases, operational semantics, and (therefore) syntaxes, though -- this thread makes that pretty clear.

My perception is that different people were had primary interests in the different use cases. Just because we may be on the path of a solution to the cascaded expression use cases doesn't mean there isn't still interest and support for the definitional use cases.

Who said there isn't "interest"? We're trying to keep what's in ES6 consistent, which is why I used "deferred". I'd be happy deferring beyond strawman at this point. We need to button down ES6.

Here is what I see. At the meeting, we decide to look at a "mustache" that addressed two generally orthogonal sets of use cases. We looked at that and found significant issues in using one construct for both sets of use cases. We then saw how we could use distinct syntax for the two sets of use cases and avoid those issues. However, at that point some of us, who seem primarily interest in only one of the use case sets seem to be essentially saying (just my perception), ok I have may solution, let's blow off the other set of use cases as they aren't very important.

I think this is all moving a bit too fast. there are a lot of good ideas over the last couple days and we need to be careful to start playing wack-a-mole with them.

# Brendan Eich (12 years ago)

Allen Wirfs-Brock wrote:

Ultimately, I think we will find that we can't have a semantics like|super| that is only usable within a syntactic class definition but has no reflective support and no way way to programmatically desugar a class definition into the exact equivalent set of object definitions.

That's not so, and at last week's meeting, we discussed alternatives including gensym'ed lexical bindings used by a less local transformation-spec.

That's only half a solution.

This ignores the cost of letting super be written in any function, which we definitely discussed. Many on TC39 are concerned this is a footgun. Anyone writing super in a function may bind to the wrong object (e.g., the dictionary-parameter object containing the function-valued property, passed into an API but itself not used after the call -- see Kevin Smith's mail from a past thread this year).

We talked about all of this and more than a few people in the room objected to allowing people to write "wrong-super" (like "wrong-this" only even less useful) functions and then tell developers facing bugs to "use Object.defineMethod".

support of|super| outside of class is the only think missing to enable that.

No, there are alternatives that confine super to be usable only within classes.

So, it something I will likely continue top push on.

If the committee or significant (more than two) members are against the approach, this is not productive.

# Tab Atkins Jr. (12 years ago)

On Wed, May 30, 2012 at 5:24 PM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

Here is what I see.  At the meeting, we decide to look at a "mustache" that addressed two generally orthogonal sets of use cases.  We looked at that and found significant issues in using one construct for both sets of use cases. We then saw how we could use distinct syntax for the two sets of use cases and avoid those issues.  However, at that point some of us, who seem primarily interest in only one of the use case sets seem to be essentially saying (just my perception), ok I have may solution, let's blow off the other set of use cases as they aren't very important.

No offense taken, but in defense of my position, I'm arguing that we can toss the [[DefineOwnProperty]] from a YAGNI perspective, unless/until someone demonstrates that we do indeed need that behavior. I suspect that's also the intent of several others arguing similarly on the list.

(I can personally get behind a suggestion to start by inventing a less cumbersome [[DOP]] API than Object.defineProperties, and see if there's an uptick in use. If so, we can then come back and bless some syntax for it as necessary.)

However, I'm not a committee member, just a sideline commenter. ^_^

# Allen Wirfs-Brock (12 years ago)

On May 30, 2012, at 5:14 PM, Brendan Eich wrote:

Allen Wirfs-Brock wrote:

I now could be quite happy with the paren based cascade idea that Brendan suggested last night. I'm surprised that no one has commented on the bnf I provided in a subsequent message.

Grammar is a detail at this point. The objection to parens is reasonable enough: they look bad when split across multiple lines:

obj.( deep.( deeper: val; deeper2: val2 ) )

They look different, but it's not clear to me that it looks bad. I would use = instead of : because we are really just factoring expressions and = is the operator that we are dealing with.

There is a simple story for how:

obj.foo=val; obj.bar=val2;

factors into:

obj.( foo=val; bar=val ); or obj.(foo=val, bar=val2);

changing the = to : seems arbitrarily different hurts the simple story. No where else in the language does : mean assignment.

(assume everything doesn't fit on one line).

Schemers might style this thus:

obj.(deep.(deeper: val; deeper2: val2))

but that looks bad too -- too ragged-left with arbitrary indentation and )))) pile-ups at the end.

Braces look right because they follow K&R style, which as Crock points out wins in JS because of restricted productions, specifically

K&S style can be applied whether you use () or {} so I don't see who that is relevant.

return { obj: literal, here: right };

So,

obj.{ deep.{ deeper: val; deeper2: val2 } }

The big issue is the obj . { deeper: val; deeper2: val }

look exactly like an object literal (except for the ; which is too small of a difference to matter) but has different semantics.

# Brendan Eich (12 years ago)

Allen Wirfs-Brock wrote:

obj.( deep.( deeper: val; deeper2: val2 ) )

They look different, but it's not clear to me that it looks bad. I would use = instead of : because we are really just factoring expressions and = is the operator that we are dealing with.

Argh, I meant to use = of course. : is wrong because this is [[Put]] not [[DefineOwnProperty]].

Otherwise the objection to ( and ) as stand-ins for { and } in K&R style stands. Is it clear now?

# Allen Wirfs-Brock (12 years ago)

On May 30, 2012, at 5:31 PM, Brendan Eich wrote:

Allen Wirfs-Brock wrote:

Ultimately, I think we will find that we can't have a semantics like|super| that is only usable within a syntactic class definition but has no reflective support and no way way to programmatically desugar a class definition into the exact equivalent set of object definitions.

That's not so, and at last week's meeting, we discussed alternatives including gensym'ed lexical bindings used by a less local transformation-spec.

Sorry, I didn't mean a mechanical de-sugaring that produces into a logically equivalent semantics using different lower level construct. I mean a refactoring of the exact same function bodies into more primitive ES object/function definitions. For example:

class Foo extend Bar { constructor (z) { this.z } method m1 ( ) { return this.z } method m2 () { return this.m2() } }

as being equivalent to

let Foo = function Foo(z) { this.z } Foo.proto = Bar; Foo.prototype= { method m1 ( ) { return this.z }, method m2 () { return this.m2() } } Object.defineOwnProperty(Foo.prototype,"constructor",{value: Foo,}); Foo.prototype.proto = Bar.prototype;

This equivalence breaks down if you can't have super in any of the method bodies. You can try to create an equivalent semantics but by rewriting parts of the bodies but it probably won't be perfect (dealing with proto changes on the prototype object seems difficult).

That's only half a solution.

This ignores the cost of letting super be written in any function, which we definitely discussed. Many on TC39 are concerned this is a footgun. Anyone writing super in a function may bind to the wrong object (e.g., the dictionary-parameter object containing the function-valued property, passed into an API but itself not used after the call -- see Kevin Smith's mail from a past thread this year).

We talked about all of this and more than a few people in the room objected to allowing people to write "wrong-super" (like "wrong-this" only even less useful) functions and then tell developers facing bugs to "use Object.defineMethod".

I agree, Object.defineMethod is a foot gun. I don't don't agree obj.{method() {super()} } seems like a much safer way to express the samething.

support of|super| outside of class is the only think missing to enable that.

No, there are alternatives that confine super to be usable only within classes.

yes, but with the loss of decomposability as outlined above.

So, it something I will likely continue top push on.

If the committee or significant (more than two) members are against the approach, this is not productive.

Yes, its all about finding consensus.

But, if you don't play you can't win...

# Allen Wirfs-Brock (12 years ago)

On May 30, 2012, at 5:33 PM, Tab Atkins Jr. wrote:

On Wed, May 30, 2012 at 5:24 PM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

Here is what I see. At the meeting, we decide to look at a "mustache" that addressed two generally orthogonal sets of use cases. We looked at that and found significant issues in using one construct for both sets of use cases. We then saw how we could use distinct syntax for the two sets of use cases and avoid those issues. However, at that point some of us, who seem primarily interest in only one of the use case sets seem to be essentially saying (just my perception), ok I have may solution, let's blow off the other set of use cases as they aren't very important.

No offense taken, but in defense of my position, I'm arguing that we can toss the [[DefineOwnProperty]] from a YAGNI perspective, unless/until someone demonstrates that we do indeed need that behavior. I suspect that's also the intent of several others arguing similarly on the list.

I think the issue is more about consistency than about YAGNI

If you have object literals and mustache as a way to add additional property definitions to an object. Then it seems highly desirable that

let obj = { p1: 1, p2 :2, m1 () {}, p3: 3, m2 () {} };

evaluate to essentially the same thing as:

let obj = { p1: 1, p2 :2, m1 () {} }.{ p3: 3, m2 () {} };

Object literals use [[DOP]] for all property definitions so to get this equivalence mustashe need to too.

# Allen Wirfs-Brock (12 years ago)

On May 30, 2012, at 5:43 PM, Brendan Eich wrote:

Allen Wirfs-Brock wrote:

obj.( deep.( deeper: val; deeper2: val2 ) )

They look different, but it's not clear to me that it looks bad. I would use = instead of : because we are really just factoring expressions and = is the operator that we are dealing with.

Argh, I meant to use = of course. : is wrong because this is [[Put]] not [[DefineOwnProperty]].

Otherwise the objection to ( and ) as stand-ins for { and } in K&R style stands. Is it clear now?

Yes, that better. However, I still think there is a difference of option between: 1) using ( ) looks ugly, it isn't in the syntactic style of K&R
2) there are already too many different uses of { } in ES, another will just add confusion

These are both subjective judgments.

# Brendan Eich (12 years ago)

Allen Wirfs-Brock wrote:

These are both subjective judgments.

What, other than grammatical ambiguity or other soundness problems, is not in this kind of design?

# Herby Vojčík (12 years ago)

Allen Wirfs-Brock wrote:

On May 30, 2012, at 4:42 PM, Brendan Eich wrote:

What we agreed to last week: no |super| outside of classes in ES6, no Object.defineMethod in ES6. That's what I recall and what the notes say (re: |super|).

I also don't see the mention of defineMethod although I know we talked about it.

I agree that we have a deferral of super/defineMethod but I personally don't think the former is going to stay deferred, if we reach agreement on classes that have super.

Ultimately, I think we will find that we can't have a semantics like |super| that is only usable within a syntactic class definition but has no reflective support and no way way to programmatically desugar a class definition into the exact equivalent set of object definitions. That's only half a solution. support of |super| outside of class is the only think missing to enable that. So, it something I will likely continue top push on.

In that light mustache with super containing concise methods seems like a very good way to avoid the defineMethod usability issues.

+a lot!

# Andreas Rossberg (12 years ago)

On 30 May 2012 22:33, Mark S. Miller <erights at google.com> wrote:

My concern is indeed the overall complexity budget. And I agree. I'm happy to have both of these if we can make some real complexity cuts elsewhere. I look forward to that other conversation ;).

I have the same concern. The syntactic complexity and subtlety of this proposal seems relatively high for the limited value it provides. If we are going to have class syntax anyway, I doubt that mustache or any of its variants will see enough use to warrant adding it as well.

# Andreas Rossberg (12 years ago)

On 30 May 2012 23:34, David Herman <dherman at mozilla.com> wrote:

On May 30, 2012, at 2:27 PM, Rick Waldron wrote:

On Wed, May 30, 2012 at 5:22 PM, David Herman <dherman at mozilla.com> wrote:

array.{        pop();        pop();        pop();    };

path.{        moveTo(10, 10);        stroke("red");        fill("blue");        ellipse(50, 50);    };

this.{        foo = 17;        bar = "hello";        baz = true;    };

This is beautiful and looks powerful -- will it still meet the most common/obvious use case?

var div = document.createElement().

div.{   style.{     color = "red";     left = "100px";   } };

I didn't specify in my blog post. :) I'm open to it. I tend to find the nested syntax a little brain-exploding, but I understand the motivation.

Assuming you propose to allow things like o.{a.b = 6; c.f()} anyway, then the above would actually fall out for free if you defined your syntax as pure syntactic sugar, and do the rewriting in the natural bottom up manner.

# T.J. Crowder (12 years ago)

On 31 May 2012 10:44, Andreas Rossberg <rossberg at google.com> wrote:

On 30 May 2012 23:34, David Herman <dherman at mozilla.com> wrote:

On May 30, 2012, at 2:27 PM, Rick Waldron wrote:

On Wed, May 30, 2012 at 5:22 PM, David Herman <dherman at mozilla.com> wrote:

array.{ pop(); pop(); pop(); };

path.{ moveTo(10, 10); stroke("red"); fill("blue"); ellipse(50, 50); };

this.{ foo = 17; bar = "hello"; baz = true; };

This is beautiful and looks powerful -- will it still meet the most common/obvious use case?

var div = document.createElement().

div.{ style.{ color = "red"; left = "100px"; } };

I didn't specify in my blog post. :) I'm open to it. I tend to find the nested syntax a little brain-exploding, but I understand the motivation.

Assuming you propose to allow things like o.{a.b = 6; c.f()} anyway, then the above would actually fall out for free if you defined your syntax as pure syntactic sugar, and do the rewriting in the natural bottom up manner.

/Andreas

I'm worried this looks a lot like with -- with the same issues. Is the idea that unqualified references would be required to be properties of the object in question? So

o.{ a.b = 6; c.f(); };

...would work, but

o.{ a.b = foo; c.f(); };

...would throw (assuming there is no foo property)?

I keep meaning to propose a new with (different keyword, obviously) that solves the with issues by using a leading dot -- but I wanted to lurk a while beforehand, can't imagine it's an original thought (esp. as I used some language in the 90s that did that).

-- T.J.

# Jussi Kalliokoski (12 years ago)

On Thu, May 31, 2012 at 12:52 PM, T.J. Crowder <tj at crowdersoftware.com>wrote:

On 31 May 2012 10:44, Andreas Rossberg <rossberg at google.com> wrote:

On 30 May 2012 23:34, David Herman <dherman at mozilla.com> wrote:

On May 30, 2012, at 2:27 PM, Rick Waldron wrote:

On Wed, May 30, 2012 at 5:22 PM, David Herman <dherman at mozilla.com> wrote:

array.{ pop(); pop(); pop(); };

path.{ moveTo(10, 10); stroke("red"); fill("blue"); ellipse(50, 50); };

this.{ foo = 17; bar = "hello"; baz = true; };

This is beautiful and looks powerful -- will it still meet the most common/obvious use case?

var div = document.createElement().

div.{ style.{ color = "red"; left = "100px"; } };

I didn't specify in my blog post. :) I'm open to it. I tend to find the nested syntax a little brain-exploding, but I understand the motivation.

Assuming you propose to allow things like o.{a.b = 6; c.f()} anyway, then the above would actually fall out for free if you defined your syntax as pure syntactic sugar, and do the rewriting in the natural bottom up manner.

/Andreas

I'm worried this looks a lot like with -- with the same issues. Is the idea that unqualified references would be required to be properties of the object in question? So

I actually thought that this looks like with as well, but I don't share your concerns, to me this looks like a fixed with.

o.{ a.b = 6; c.f(); };

...would work, but

o.{ a.b = foo; c.f(); };

...would throw (assuming there is no foo property)?

I don't see a reason why it wouldn't throw, if I understood correctly. I don't see why foo should have anything to do with o. IMHO, it's best if only the left-hand side interacts with the object being extended, this way we don't get the problems of with, i.e. scope being (near-)impossible to measure and more minorly, easy leaked globals (e.g. var a = {}; with (a) { foo = 'bar' } ).

# François REMY (12 years ago)

From: T.J. Crowder

I'm worried this looks a lot like with -- with the same issues. Is the idea that unqualified references would be required to be properties of the object in question?

This looks like a with regarding some points, but this is cleary not a with. I think

var style = "color: red";
var elem = document.createElement("span");

elem.(
    className = "hello";
    style = style;
    id = elem.uniqueID;
);

would be simply rewritten

var style = "color: red";
var elem = document.createElement("span");

elem.className = "hello";
elem.style = style;
elem.id = elem.uniqueID;

Am I right here?

# T.J. Crowder (12 years ago)

On 31 May 2012 11:05, Jussi Kalliokoski <jussi.kalliokoski at gmail.com> wrote:

On Thu, May 31, 2012 at 12:52 PM, T.J. Crowder <tj at crowdersoftware.com>wrote:

On 31 May 2012 10:44, Andreas Rossberg <rossberg at google.com> wrote:

On 30 May 2012 23:34, David Herman <dherman at mozilla.com> wrote:

On May 30, 2012, at 2:27 PM, Rick Waldron wrote:

On Wed, May 30, 2012 at 5:22 PM, David Herman <dherman at mozilla.com> wrote:

array.{ pop(); pop(); pop(); };

path.{ moveTo(10, 10); stroke("red"); fill("blue"); ellipse(50, 50); };

this.{ foo = 17; bar = "hello"; baz = true; };

This is beautiful and looks powerful -- will it still meet the most common/obvious use case?

var div = document.createElement().

div.{ style.{ color = "red"; left = "100px"; } };

I didn't specify in my blog post. :) I'm open to it. I tend to find the nested syntax a little brain-exploding, but I understand the motivation.

Assuming you propose to allow things like o.{a.b = 6; c.f()} anyway, then the above would actually fall out for free if you defined your syntax as pure syntactic sugar, and do the rewriting in the natural bottom up manner.

/Andreas

I'm worried this looks a lot like with -- with the same issues. Is the idea that unqualified references would be required to be properties of the object in question? So

I actually thought that this looks like with as well, but I don't share your concerns, to me this looks like a fixed with.

o.{ a.b = 6; c.f(); };

...would work, but

o.{ a.b = foo; c.f(); };

...would throw (assuming there is no foo property)?

I don't see a reason why it wouldn't throw, if I understood correctly. I don't see why foo should have anything to do with o. IMHO, it's best if only the left-hand side interacts with the object being extended, this way we don't get the problems of with, i.e. scope being (near-)impossible to measure and more minorly, easy leaked globals (e.g. var a = {}; with (a) { foo = 'bar' } ).

Cheers, Jussi

So the idea is that only the left-hand side has the magic. Except that there doesn't always seem to be a left-hand side (ref c.f() above), which makes this look at lot like some kind of block rather than a batch assignment/definition (since things aren't always being assigned/defined), and thus a lot like with.

If we start introducing calls as well as assignments and property definitions. When things start looking like a series of statements, I'm worried we'll start reading them that way, and get things like this:

o.{ a.b = 6; c.f(); a.b = a.c; // !! };

The expectation there (the last line) would surely be that both as refer to the same thing. This problem exists now (and sometimes comes up) with regard to object literals, but object literals don't look much like a series of statements. This does.

-- T.J.

# T.J. Crowder (12 years ago)

On 31 May 2012 11:18, T.J. Crowder <tj at crowdersoftware.com> wrote:

On 31 May 2012 11:05, Jussi Kalliokoski <jussi.kalliokoski at gmail.com>wrote:

On Thu, May 31, 2012 at 12:52 PM, T.J. Crowder <tj at crowdersoftware.com>wrote:

On 31 May 2012 10:44, Andreas Rossberg <rossberg at google.com> wrote:

On 30 May 2012 23:34, David Herman <dherman at mozilla.com> wrote:

On May 30, 2012, at 2:27 PM, Rick Waldron wrote:

On Wed, May 30, 2012 at 5:22 PM, David Herman <dherman at mozilla.com> wrote:

array.{ pop(); pop(); pop(); };

path.{ moveTo(10, 10); stroke("red"); fill("blue"); ellipse(50, 50); };

this.{ foo = 17; bar = "hello"; baz = true; };

This is beautiful and looks powerful -- will it still meet the most common/obvious use case?

var div = document.createElement().

div.{ style.{ color = "red"; left = "100px"; } };

I didn't specify in my blog post. :) I'm open to it. I tend to find the nested syntax a little brain-exploding, but I understand the motivation.

Assuming you propose to allow things like o.{a.b = 6; c.f()} anyway, then the above would actually fall out for free if you defined your syntax as pure syntactic sugar, and do the rewriting in the natural bottom up manner.

/Andreas

I'm worried this looks a lot like with -- with the same issues. Is the idea that unqualified references would be required to be properties of the object in question? So

I actually thought that this looks like with as well, but I don't share your concerns, to me this looks like a fixed with.

o.{ a.b = 6; c.f(); };

...would work, but

o.{ a.b = foo; c.f(); };

...would throw (assuming there is no foo property)?

I don't see a reason why it wouldn't throw, if I understood correctly. I don't see why foo should have anything to do with o. IMHO, it's best if only the left-hand side interacts with the object being extended, this way we don't get the problems of with, i.e. scope being (near-)impossible to measure and more minorly, easy leaked globals (e.g. var a = {}; with (a) { foo = 'bar' } ).

Cheers, Jussi

So the idea is that only the left-hand side has the magic. Except that there doesn't always seem to be a left-hand side (ref c.f() above), which makes this look at lot like some kind of block rather than a batch assignment/definition (since things aren't always being assigned/defined), and thus a lot like with.

If we start introducing calls as well as assignments and property definitions. When things start looking like a series of statements, I'm worried we'll start reading them that way, and get things like this:

o.{ a.b = 6; c.f(); a.b = a.c; // !! };

The expectation there (the last line) would surely be that both as refer to the same thing. This problem exists now (and sometimes comes up) with regard to object literals, but object literals don't look much like a series of statements. This does.

-- T.J.

I should clarify here that I don't think the current strawman allows that c.f() line. It's something that's been raised in this thread.

-- T.J.

# François REMY (12 years ago)

o.{ a.b = 6; c.f(); a.b = a.c; // !! };

This is indeed an issue that could be solved using

o.{
    .a.b = 6;
    .c.f();
    .a.b = .a.c;
}

My previous sample would be rewritten :

var style = "color: red";
var elem = document.createElement("span");

elem.{
    .className = "hello";
    .style = style;
    .id = .uniqueID;
};

This looks a lot like the C# syntax of the 'with' statement, which is maybe a good idea.

dynamic obj = new ... with {
    .a = "hello";
    .b = inScopeVar;
    .c = .a + .b;
};
# T.J. Crowder (12 years ago)

On 31 May 2012 11:30, François REMY <fremycompany_pub at yahoo.fr> wrote:

o.{

a.b = 6; c.f(); a.b = a.c; // !! };

This is indeed an issue that could be solved using

o.{ .a.b = 6; .c.f(); .a.b = .a.c; }

Exactly my thought, and as it happens I was just writing it up: See my "'with' revisited and related to object extension literals".

-- T.J.

# Aymeric Vitte (12 years ago)

I really like(d) the mustache proposal but unfortunately reached the same conclusion : too complicate and confusing for what it brings. It's surprising that the fundamental difference between Put and DOP was unknown from almost everybody (including myself), then a rapid and easy conclusion knowing that DOP is not very used could be : eliminate DOP, but it's not possible, one of its purpose is to avoid modifying again the prototype properties as could do the json attack described in the posts.

Anyway, the discussion slipped to the technical aspects only, then before imagining tons of syntax possibilities, maybe the use should be considered (normal use, not super and more complicated things), simple example :

div.style.{width:0,cssText='xxx'} or div.style.{width:0,cssText:'xxx'} ?

How from a developper perspective do I know if cssText was defined inside CSSStyleDeclaration prototype or via outside getters/setters ??? Will I have to think about that each time I am using mustache ?

Then to secure it I would be enclined to use always "=", which could look strange and confusing to normal developpers.

It's true that it can be compared somewhere to 'with' statement which is almost never used because maybe not so usefull and everybody stated that you can easily make a mistake, maybe the same may apply to mustache

Le 31/05/2012 11:35, Andreas Rossberg a écrit :

# Rick Waldron (12 years ago)

On Wed, May 30, 2012 at 8:40 PM, Allen Wirfs-Brock <allen at wirfs-brock.com>wrote:

On May 30, 2012, at 5:14 PM, Brendan Eich wrote:

Allen Wirfs-Brock wrote:

I now could be quite happy with the paren based cascade idea that Brendan suggested last night. I'm surprised that no one has commented on the bnf I provided in a subsequent message.

Grammar is a detail at this point. The objection to parens is reasonable enough: they look bad when split across multiple lines:

obj.( deep.( deeper: val; deeper2: val2 ) )

They look different, but it's not clear to me that it looks bad. I would use = instead of : because we are really just factoring expressions and = is the operator that we are dealing with.

There is a simple story for how:

obj.foo=val; obj.bar=val2;

factors into:

obj.( foo=val; bar=val ); or obj.(foo=val, bar=val2);

Parens with comma separation might be too visually similar to this:

function obj(a, b) { console.log(a, b); }

obj(foo="1", bar="2")

1 2

changing the = to : seems arbitrarily different hurts the simple story. No where else in the language does : mean assignment.

(assume everything doesn't fit on one line).

Schemers might style this thus:

obj.(deep.(deeper: val; deeper2: val2))

but that looks bad too -- too ragged-left with arbitrary indentation and )))) pile-ups at the end.

Braces look right because they follow K&R style, which as Crock points out wins in JS because of restricted productions, specifically

K&S style can be applied whether you use () or {} so I don't see who that is relevant.

return { obj: literal, here: right };

So,

obj.{ deep.{ deeper: val; deeper2: val2 } }

The big issue is the obj . { deeper: val; deeper2: val }

I absolutely agree, but change it to = and it becomes distinguishable (opinion, of course ;D )

obj. { deeper = "1"; deeper = "2" }

# Rick Waldron (12 years ago)

On Thu, May 31, 2012 at 6:05 AM, Jussi Kalliokoski < jussi.kalliokoski at gmail.com> wrote:

On Thu, May 31, 2012 at 12:52 PM, T.J. Crowder <tj at crowdersoftware.com>wrote:

On 31 May 2012 10:44, Andreas Rossberg <rossberg at google.com> wrote:

On 30 May 2012 23:34, David Herman <dherman at mozilla.com> wrote:

On May 30, 2012, at 2:27 PM, Rick Waldron wrote:

On Wed, May 30, 2012 at 5:22 PM, David Herman <dherman at mozilla.com> wrote:

array.{ pop(); pop(); pop(); };

path.{ moveTo(10, 10); stroke("red"); fill("blue"); ellipse(50, 50); };

this.{ foo = 17; bar = "hello"; baz = true; };

This is beautiful and looks powerful -- will it still meet the most common/obvious use case?

var div = document.createElement().

div.{ style.{ color = "red"; left = "100px"; } };

I didn't specify in my blog post. :) I'm open to it. I tend to find the nested syntax a little brain-exploding, but I understand the motivation.

Assuming you propose to allow things like o.{a.b = 6; c.f()} anyway, then the above would actually fall out for free if you defined your syntax as pure syntactic sugar, and do the rewriting in the natural bottom up manner.

/Andreas

I'm worried this looks a lot like with -- with the same issues. Is the idea that unqualified references would be required to be properties of the object in question? So

I actually thought that this looks like with as well, but I don't share your concerns, to me this looks like a fixed with.

o.{ a.b = 6; c.f(); };

...would work, but

o.{ a.b = foo; c.f(); };

...would throw (assuming there is no foo property)?

I don't see a reason why it wouldn't throw, if I understood correctly. I don't see why foo should have anything to do with o. IMHO, it's best if only the left-hand side interacts with the object being extended, this way we don't get the problems of with, i.e. scope being (near-)impossible to measure and more minorly, easy leaked globals (e.g. var a = {}; with (a) { foo = 'bar' } ).

I would expect it to throw a ReferenceError: foo is not defined

# David Herman (12 years ago)

On May 31, 2012, at 2:52 AM, T.J. Crowder wrote:

I'm worried this looks a lot like with -- with the same issues.

Nooooo. No.

This does not involve the object with the scope chain in any way.

Is the idea that unqualified references would be required to be properties of the object in question? So

o.{ a.b = 6; c.f(); };

...would work, but

o.{ a.b = foo; c.f(); };

...would throw (assuming there is no foo property)?

No. The object is used for the (syntactically restricted, i.e., non-expression) LHS, but not the RHS expression. The object is not added to the scope chain.

I keep meaning to propose a new with (different keyword, obviously) that solves the with issues by using a leading dot -- but I wanted to lurk a while beforehand, can't imagine it's an original thought (esp. as I used some language in the 90s that did that).

You're thinking of VB. I agree it was a better design than JS's with. But it's unfortunately a non-starter for adding to JS, because it introduces a new ASI hazard:

with (obj) {
    foo() // no semi
    .bar = 12
}
# Brendan Eich (12 years ago)

Thanks, Dave.

Bottom-citing here, and again I'm a sinner too, but could everyone on this nicely long thread take a breath and remember two things:

  1. We aren't going to reform 'with' for ES6 or Harmony. It's banned in strict mode, there due to 1JS in the non-strict mode of the language, not to be fiddled with. None of the experienced folks here are suggesting any objects-on-the-scope-chain, as Dave notes.

  2. Gmail seems to cause massive top-citing. Trim for context, most mail user agents do not collapse, or if they do, the reader is left without usefully short context when uncollapsing to see what the message is citing in reply.

# T.J. Crowder (12 years ago)

On 31 May 2012 19:21, Brendan Eich <brendan at mozilla.org> wrote:

Thanks, Dave.

Bottom-citing here, and again I'm a sinner too, but could everyone on this nicely long thread take a breath and remember two things:

  1. We aren't going to reform 'with' for ES6 or Harmony. It's banned in strict mode, there due to 1JS in the non-strict mode of the language, not to be fiddled with. None of the experienced folks here are suggesting any objects-on-the-scope-chain, as Dave notes.

And if that's the decision, that's fine. Note that I wasn't (and don't) advocate reviving the old with.

My point is that the discussion of object extension literals was clearly headed toward being a new with, esp. with the introduction (I'm not sure from whom) of function calls. It was headed toward being "with-lite". So my recommendation is either bite the bullet, or let it go. I'm not seeing the value of a half-with a'la the current strawman. Happy to be persuaded, but not seeing it, esp. in light of the [[DefineOwnProperty]] vs. [[Put]] situation. My suggestion (in the other thread) addresses that by encompassing the full spectrum, with an explicit and distinct syntax.

(What's meant to be the convention here? I've been trimming and typing under what I cite. I'm seeing a complete mish-mash of styles and not sure which you were asking us to use. Keen to go with the flow.)

-- T.J.

# Brendan Eich (12 years ago)

T.J. Crowder wrote:

My point is that the discussion of object extension literals was clearly headed toward being a new with,

No, this is what Dave was objecting to. It's careless to say "new 'with'" if you do not mean dynamic scope, specifically object on the scope chain. No one meant that, and cascaded (Smalltalk "fluent style") call chains do not use the scope chain in any proposal.