Comments on Sept Meeting Notes

# Kevin Smith (8 years ago)

Thanks Rick, for writing up these meeting notes once again. Some comments:

Symbols

As it currently stands, the only thing that symbols provide is an isolated namespace for meta-level property keys. This assures us that arbitrary string keys will not collide with any meta-level property names. But going forward, arbitrary string keys should be stored in a Map, not as properties on an object.

Instead of symbols, a namespaced, non-identifier string key can be used for meta-level property names (e.g.):

class C {
  "std:iterator"() { ... }
}

This is more convenient than symbols, serves the purpose equally well, and it requires no additional primitive types. In addition, string keys require no special-casing for representation in debugging tools. Simpler is better-er.

Destructuring Semantics

Having two distinct pattern sublanguage semantics (i.e. one for destructuring and one for pattern matching) is, well, nuts. The fact the refutable patterns are new to JS programmers is not sufficient reason to abandon them. The real problem here is the false dilemma introduced by deferring essential features. In this case, the essential feature is the irrefutability switch ("?"). We do not have a complete destructuring feature without it.

Design Process Changes

The slide deck on process change was my favorite part of the notes. I completely agree that a more formal process would be good for all. In my opinion, modules should be the first feature that is pushed through a more formal process. It is abundantly clear to me that modules are not mature enough to be completed within the next three months, and the right thing to do is to free modules from the timeframe imposed by "ES6".

# Tab Atkins Jr. (8 years ago)

On Tue, Sep 24, 2013 at 6:49 PM, Kevin Smith <zenparsing at gmail.com> wrote:

Symbols

As it currently stands, the only thing that symbols provide is an isolated namespace for meta-level property keys. This assures us that arbitrary string keys will not collide with any meta-level property names. But going forward, arbitrary string keys should be stored in a Map, not as properties on an object.

This seems like a non-sequitur. Symbols aren't meant to help with the "object as map" use-case, and even if you tried to, they work terribly for it. They're meant for the "add an actual property/method without collision" use-case. Again, Maps seem like a non-sequitur here - using a Map doesn't aid with avoiding collisions.

Instead of symbols, a namespaced, non-identifier string key can be used for meta-level property names (e.g.):

class C {
  "std:iterator"() { ... }
}

This is more convenient than symbols, serves the purpose equally well, and it requires no additional primitive types. In addition, string keys require no special-casing for representation in debugging tools. Simpler is better-er.

How is this in any way better than:

class C {
  std_iterator() { ... }
}

?

~TJ

# Kevin Smith (8 years ago)

This seems like a non-sequitur. Symbols aren't meant to help with the "object as map" use-case, and even if you tried to, they work terribly for it. They're meant for the "add an actual property/method without collision" use-case. Again, Maps seem like a non-sequitur here - using a Map doesn't aid with avoiding collisions.

Before Maps, I might use a regular object to store arbitrary string-to-value mappings. Say I'm counting the occurrences of terms appearing on es-discuss. The key "std:iterator" might actually occur. In that case we would have a collision with my proposed meta-level property name. But going forward, no one's going to use a regular object for arbitrary string keys - it's simply too error-prone. They'll use a Map instead. Which means that we don't have to worry about arbitrary string keys colliding with meta-level property names.

Now, one might argue that using the string "std:iterator" (or equivalent) would present a backward compatibility hazard for legacy code using objects as maps. I'll have to think about that one...

The argument is that, once you take out arbitrary string keys (as occur when using an object as map), a "namespaced" string provides sufficient collision resistance to make symbols unnecessary.

How is this in any way better than:

class C { std_iterator() { ... } }

The set of non-identifiers is less commonly used for member names by far, so it has that advantage. But I'm not really saying that "std:iterator" is better than "std_iterator". I'm saying that both are a simpler solution than symbols.

# Brendan Eich (8 years ago)

Kevin Smith wrote:

Now, one might argue that using the string "std:iterator" (or equivalent) would present a backward compatibility hazard for legacy code using objects as maps. I'll have to think about that one...

Problem is polyfillability, and Map polyfill with O(n^2) complexity is a loser in general. People want to use symbol in object. Downrev can mock symbols with randomized strings.

# Kevin Smith (8 years ago)

Now, one might argue that using the string "std:iterator" (or equivalent)

would present a backward compatibility hazard for legacy code using objects as maps. I'll have to think about that one...

Problem is polyfillability, and Map polyfill with O(n^2) complexity is a loser in general. People want to use symbol in object. Downrev can mock symbols with randomized strings.

Also, form[*], document.all[*], etc, will all continue to use arbitrary element IDs as property names, which may bear on the argument (I'm not sure how, yet).

# Domenic Denicola (8 years ago)

I do not plan to switch to Map for string-keyed maps. I’ll be using Object.create(null) instead.

# Dean Landolt (8 years ago)

You call namespaced strings "more convenient than symbols, serves the purpose equally well". These two things are obviously not equivalent -- namespaced strings are obviously weaker. We could use them today, yet hardly anyone does this, because it's inconvenient in it's own ways. Yet name collisions happen all the time in the wild, even with ad hoc namespaces, and this is the problem symbols completely solve. Ad hoc namespaces can't solve the collision problem and obviously don't solve convenience problem -- if they did, we'd already be using them.

# Andrea Giammarchi (8 years ago)

about that, not sure it's fully aligned with latest specs but it was behaving correctly and Brandon Benvie confirmed it a while ago:

Symbol simplified polyfill for ES5

# Kevin Smith (8 years ago)

I do not plan to switch to Map for string-keyed maps. I’ll be using Object.create(null) instead.

Thanks, Domenic. I withdraw that aspect of the argument.

I'm still not quite convinced that objects-as-maps make a truly isolated namespace necessary, however. I would be convinced by a code example showing how a property of an object using arbitrary string keys could be misinterpreted as a meta-level property.

I'll try to think of one...

# Domenic Denicola (8 years ago)

From: Kevin Smith [zenparsing at gmail.com]

I'm still not quite convinced that objects-as-maps make a truly isolated namespace necessary, however. I would be convinced by a code example showing how a property of an object using arbitrary string keys could be misinterpreted as a meta-level property.

I'll try to think of one...

The hard part of producing such examples is that most of the meta-level properties are functions (e.g. iterator), and thus it's not trivially easy to produce an object from JSON.parseing user input. But there are some meta-level properties that are not functions, namely @@isRegExp, @@toStringTag, and @@unscopables.

So let's say that we decided to use a non-isolated namespace of strings, instead of unique symbols. Thus, we would have "std:isRegExp", "std:toStringTag", and "std:unscopeables". Well, then simple code like this:

var requestBody = JSON.parse(req.body);

could end up getting a very weird object, if I POSTed the string

{
  "std:isRegExp": true,
  "std:toStringTag": "My Custom String Tag With Spaces and Punctuation!",
  "std:unscopeables": ["hasOwnProperty", "toString", "propertyIsEnumerable"]
}

to that HTTP endpoint.

# Kevin Smith (8 years ago)

You call namespaced strings "more convenient than symbols, serves the purpose equally well". These two things are obviously not equivalent -- namespaced strings are obviously weaker.

Obviously. I don't contend otherwise. I'm asking a different question: does their added "strength" justify the added complexity which symbols bring to the object model? The burden of proof lies with those wanting to extend the object model, not the other way around.

We could use them today, yet hardly anyone does this, because it's inconvenient in it's own ways.

How are namespaced strings inconvenient other than being a non-identifier, or perhaps a longish identifier?

Yet name collisions happen all the time in the wild, even with ad hoc namespaces,

Can you provide an example of this?

and this is the problem symbols completely solve. Ad hoc namespaces *can't

  • solve the collision

They cannot guarantee collision avoidance with arbitrary strings, sure. But is that even a requirement? Why? That's the question I'm posing.

and obviously don't solve convenience problem -- if they did, we'd already be using them.

That doesn't constitute proof, though, does it? : )

# Brandon Benvie (8 years ago)

On 9/25/2013 11:47 AM, Kevin Smith wrote:

How are namespaced strings inconvenient other than being a non-identifier, or perhaps a longish identifier?

They are visible to enumeration.

# Kevin Smith (8 years ago)

I think that your example might do it, although I'll have to think more about it tonight.

If so, then the justification for symbols at this point is based on the dual use of JS objects as programming abstractions and as a key-value data structure. Oh, javascript... : )

# Kevin Smith (8 years ago)

After thinking this over, I still remain unconvinced that symbols are the right solution to the problem. Besides the fact that they introduce (ever more) complexity to the object model, they simply do not work as a duck-typing strategy for the real, multi-realm world of Javascript. Sure, we can make a special case of standard library symbols such that they maintain identity across realms. But a solution which only works for special cases isn't a very good solution, is it?

Here is another proposal which avoids these pitfalls:

  1. Identify all meta-layer hooks using a string that is not an identifier. The actual string used doesn't matter in this proposal, but for illustration purposes I'll use an "@"-prefix.

  2. Define all meta-layer hooks as functions. Testing for the hook will involve [[Get]] followed by [[IsCallable]].

For example, defining an iterable:

class C { "@iterator"(...args) { /* ... */ } }

Overriding the string tag:

class C { "@toStringTag"() { return "[object C]"; } }
  • Since the property name is a non-identifer, it is unlikely to collide with any object members.
  • Since the value of the hook must be a function, it is unlikely to collide with keys in an object-as-map (e.g. a JSON object).
  • Since it is just a string, it requires no changes to property semantics, and it trivially works across realms.
# Brendan Eich (8 years ago)

@ is the new dunder -- dunder at -- dat.

Among the no-symbol proposals, I like this best. (GUIDs, shudder.)

# Rick Waldron (8 years ago)

On Thu, Sep 26, 2013 at 9:11 AM, Kevin Smith <zenparsing at gmail.com> wrote:

After thinking this over, I still remain unconvinced that symbols are the right solution to the problem. Besides the fact that they introduce (ever more) complexity to the object model, they simply do not work as a duck-typing strategy for the real, multi-realm world of Javascript. Sure, we can make a special case of standard library symbols such that they maintain identity across realms. But a solution which only works for special cases isn't a very good solution, is it?

The consensus included Allen writing proposal for user-defined well-known symbol registration—these symbols would also maintain identity across realms.

rwaldron/tc39-notes/blob/master/es6/2013-09/sept-18.md#consensusresolution-1

Here is another proposal which avoids these pitfalls:

  1. Identify all meta-layer hooks using a string that is not an identifier. The actual string used doesn't matter in this proposal, but for illustration purposes I'll use an "@"-prefix.

  2. Define all meta-layer hooks as functions. Testing for the hook will involve [[Get]] followed by [[IsCallable]].

For example, defining an iterable:

class C { "@iterator"(...args) { /* ... */ } }

Overriding the string tag:

class C { "@toStringTag"() { return "[object C]"; } }
  • Since the property name is a non-identifer, it is unlikely to collide with any object members.

But these can still easily collide with <identifier-name-string> property access (of the same string name), so I don't understand how this is better, can you clarify?

  • Since the value of the hook must be a function, it is unlikely to collide

with keys in an object-as-map (e.g. a JSON object).

What about something like this?

// event.js
let events = Symbol();

export class Emitter {
  constructor() {
    this[events] = Object.create(null);
  }
  on(type, handler) {...},
  off(type, handler) {...}
  emit(type, ...args) {...}
}

// program.js
import { Emitter } from "event";

class Serialport extends Emitter {
  constructor() {
    super();

    // there is no way to accidentally pave
    // over the `events` symbol property
  }
}

If I try that with your proposal:

// event.js
export class Emitter {
  constructor() {
    this["@events"] = Object.create(null);
  }
  on(type, handler) {...},
  off(type, handler) {...}
  emit(type, ...args) {...}
}

// program.js
import { Emitter } from "Event";

class Serialport extends Emitter {
  constructor() {
    super();

    this["@events"] = "MINE";
  }
}
# Allen Wirfs-Brock (8 years ago)

On Sep 26, 2013, at 1:59 PM, Brendan Eich wrote:

@ is the new dunder -- dunder at -- dat.

Among the no-symbol proposals, I like this best. (GUIDs, shudder.)

/be

I shudder to say this, having just finished my third complete redo of Symbols in the ES6 draft, but I also like this proposal a lot.

I think I had a blind spot about string literals being accepted as methods names in object literals and classes. But this would be a significant simplifications that retains almost all the benefits of Symbols. And it's completely polyfillable.

A couple more thoughts:

Symbol-keyed property definitions were the primary motivator for "computed property names" in object literals and classes. They could also go away for ES6 -- another simplification.

Perhaps rather than the function conventions for "@" properties , we should considerhaving them always be getter properties. But, I haven't yet sold myself on either one as being better.

A negative is that it was decided that concise methods definitions create enumerable properties and I don't think we really want these showing up in for-in enumerations. Maybe we would want to revisit that decisions or at least make an exception for concise methods (or accessors) defined using explicit string literal names.

# Erik Arvidsson (8 years ago)

No surprise here, but I also support using "@" methods. I'm also in favor of making methods non enumerable by default. This makes them more consistent with what we have in ES today. My only concern is that methods in the DOM are enumerable and changing that seems like a high risk.

# Rick Waldron (8 years ago)

On Thu, Sep 26, 2013 at 5:43 PM, Allen Wirfs-Brock <allen at wirfs-brock.com>wrote:

On Sep 26, 2013, at 1:59 PM, Brendan Eich wrote:

@ is the new dunder -- dunder at -- dat.

Among the no-symbol proposals, I like this best. (GUIDs, shudder.)

/be

I shudder to say this, having just finished my third complete redo of Symbols in the ES6 draft, but I also like this proposal a lot.

I think I had a blind spot about string literals being accepted as methods names in object literals and classes. But this would be a significant simplifications that retains almost all the benefits of Symbols. And it's completely polyfillable.

A couple more thoughts:

Symbol-keyed property definitions were the primary motivator for "computed property names" in object literals and classes. They could also go away for ES6 -- another simplification.

Perhaps rather than the function conventions for "@" properties , we should considerhaving them always be getter properties. But, I haven't yet sold myself on either one as being better.

A negative is that it was decided that concise methods definitions create enumerable properties and I don't think we really want these showing up in for-in enumerations. Maybe we would want to revisit that decisions or at least make an exception for concise methods (or accessors) defined using explicit string literal names.

The whole point of making them enumerable was to undo a decision to that made an exception for concise methods—that applies to properties that were defined with string literal names ( rwaldron/tc39-notes/blob/master/es6/2012-09/sept-18.md#concise-method-definition-revisited )

var o = {
  "method": function() {}
};

Should behave exactly the same as

var o = {
  "method"() {}
};

Because existing code will expect this to work (using lodash library as an example, but it could be any library code that uses for-in):

_.extend(Some.prototype, o);

Which user code expects will result in:

Some.prototype.method === o.method; // true
# Rick Waldron (8 years ago)

On Thu, Sep 26, 2013 at 6:02 PM, Erik Arvidsson <erik.arvidsson at gmail.com>wrote:

No surprise here, but I also support using "@" methods.

I don't. Please see my response to Kevin Smith: esdiscuss/2013-September/033720

I'm also in favor of making methods non enumerable by default. This makes them more consistent with what we have in ES today.

That might be the case for methods defined on prototypes of built-in objects, but it's absolutely not the case for user land code. Please see the examples in my previous response to Allen: esdiscuss/2013-September/033725

# Allen Wirfs-Brock (8 years ago)

On Sep 26, 2013, at 3:03 PM, Rick Waldron wrote:

On Thu, Sep 26, 2013 at 5:43 PM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote: ...

A negative is that it was decided that concise methods definitions create enumerable properties and I don't think we really want these showing up in for-in enumerations. Maybe we would want to revisit that decisions or at least make an exception for concise methods (or accessors) defined using explicit string literal names.

The whole point of making them enumerable was to undo a decision to that made an exception for concise methods—that applies to properties that were defined with string literal names (rwaldron/tc39-notes/blob/master/es6/2012-09/sept-18.md#concise-method-definition-revisited)

var o = { "method": function() {} };

Should behave exactly the same as

var o = { "method"() {} };

Maybe the exception could be enumerability exception could be concise methods with string literal property names that do not parse as IdentifierName.

# Rick Waldron (8 years ago)

On Thu, Sep 26, 2013 at 6:20 PM, Allen Wirfs-Brock <allen at wirfs-brock.com>wrote:

Maybe the exception could be enumerability exception could be concise methods with string literal property names that do not parse as IdentifierName.

I'm trying to understand if there is something I'm missing about "string literal property names that do not parse as IdentifierName" that is somehow different from what exists today. What happens here:

var o = {
  "@concise"() {}
};

o["@method"] = function() {};

jQuery.extend(Some.prototype, o);

Some.prototype["@concise"] = "MINE";
# Brendan Eich (8 years ago)

Erik Arvidsson wrote:

My only concern is that methods in the DOM are enumerable and changing that seems like a high risk.

Anomalous legacy is anomalous ;-).

# Allen Wirfs-Brock (8 years ago)

On Sep 26, 2013, at 3:13 PM, Rick Waldron wrote:

That might be the case for methods defined on prototypes of built-in objects, but it's absolutely not the case for user land code. Please see the examples in my previous response to Allen: esdiscuss/2013-September/033725

so name your events property "@[email protected]".

If somebody subclasses Emitter and know that property name, then they must be doing something intentional.

Unique Symbols don't guarantee that sort of integrity. All you've accomplish by using them as in your example is to minimize that chance that somebody else doesn't accidentally use the same property name for some other purpose. Naming your property "@[email protected]" also makes such accidentally unlikely.

# Rick Waldron (8 years ago)

The Symbol isn't exposed so therefore can't accidentally be used to accidentally pave over the subclass instance object's events cache. As far as the "@[email protected]" Hungarian notation monstrosity is concerned: the first thing I thought when I saw this was that it would never pass any practitioner's peer code review. This is worse then implied collision-safety (or privacy, HA!) of "_"-prefixed properties—worse because the language is saying "go ahead and do this".

# Allen Wirfs-Brock (8 years ago)

On Sep 26, 2013, at 3:30 PM, Rick Waldron wrote:

On Thu, Sep 26, 2013 at 6:20 PM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote: ...

Maybe the exception could be enumerability exception could be concise methods with string literal property names that do not parse as IdentifierName.

I'm trying to understand if there is something I'm missing about "string literal property names that do not parse as IdentifierName" that is somehow different from what exists today. What happens here:

var o = { "@concise"() {} };

non-estensible

o["@method"] = function() {};

extensible

jQuery.extend(Some.prototype, o);

copies "@method" doesn't copy "@concise"

Some.prototype["@concise"] = "MINE";

Makes extensible "@concise" property

# David Herman (8 years ago)

On Sep 26, 2013, at 3:20 PM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

Maybe the exception could be enumerability exception could be concise methods with string literal property names that do not parse as IdentifierName.

www.youtube.com/watch?v=VX4DJUr5oYg

# Rick Waldron (8 years ago)

On Thu, Sep 26, 2013 at 6:39 PM, Allen Wirfs-Brock <allen at wirfs-brock.com>wrote:

On Sep 26, 2013, at 3:30 PM, Rick Waldron wrote:

On Thu, Sep 26, 2013 at 6:20 PM, Allen Wirfs-Brock <allen at wirfs-brock.com>wrote:

...

Maybe the exception could be enumerability exception could be concise methods with string literal property names that do not parse as IdentifierName.

I'm trying to understand if there is something I'm missing about "string literal property names that do not parse as IdentifierName" that is somehow different from what exists today. What happens here:

var o = { "@concise"() {} };

non-estensible

o["@method"] = function() {};

extensible

jQuery.extend(Some.prototype, o);

copies "@method" doesn't copy "@concise"

That's what I was afraid of... Ignoring any exceptions for concise methods, this breaks backward compatibility. Not for the author of the object, but for code that is expected to behave a certain way when it receives an object.

# Yehuda Katz (8 years ago)

I prefer Symbol names for the reasons I said at the last meeting:

  • Non-enumerability by default (the other solutions presented in this thread are finicky, harder to explain, and potentially error-prone).
  • Guaranteed duck-testability: if an object tests positive for iterability, it much be intentional. Once we start using @ as a symbolic prefix, userland libraries will likely start to use it too, making duck-tests of future names inconclusive. I'm also not convinced that @foo isn't already used for this purpose in the wild, but even if that's true, it only solves the issue for one generation.
  • Intention-preserving: You get to use a short, English name as the method name with just a bit of syntactic chrome, and you don't have to worry about the tradeoff between collisions and leaking indecipherable names to debugging.

Yehuda Katz (ph) 718.877.1325

# Allen Wirfs-Brock (8 years ago)

On Sep 26, 2013, at 3:39 PM, Rick Waldron wrote:

The Symbol isn't exposed so therefore can't accidentally be used to accidentally pave over the subclass instance object's events cache. As far as the "@RickWaldron at events" Hungarian notation monstrosity is concerned: the first thing I thought when I saw this was that it would never pass any practitioner's peer code review. This is worse then implied collision-safety (or privacy, HA!) of "_"-prefixed properties—worse because the language is saying "go ahead and do this".

Oh, that's not Hungarian notation, it's just a name space qualifier.

But, if you assume that we will added a real private state mechanism into "ES 6.1" or "ES6.2" will Symbol really carry its weight looking back 10 years from now?

# Mark S. Miller (8 years ago)

Once you add the registry, all these accidental collisions problems come back. If you don't add a registry, you lose the ability to duck type across realms.

# Domenic Denicola (8 years ago)

If it's going to be strings, it should be dunder, for consistency with the already-existing cohort of proto/[define|lookup][G|S]etter. Having two magic namespacing conventions in the language is insanity.

I don't understand why this is happening. There was fairly strong consensus on symbols at the last meeting, and nothing new has been brought to the table. Why are people's opinions suddenly changing? Vague fearmongering about "complexity"? Symbols are a good solution to a real problem, much better than strings.

# Yehuda Katz (8 years ago)

On Thu, Sep 26, 2013 at 3:47 PM, Allen Wirfs-Brock <allen at wirfs-brock.com>wrote:

But, if you assume that we will added a real private state mechanism into "ES 6.1" or "ES6.2" will Symbol really carry its weight looking back 10 years from now?

Private state doesn't satisfy these requirements because they trap on the wrong side of the proxy.

# Tab Atkins Jr. (8 years ago)

On Thu, Sep 26, 2013 at 3:48 PM, Domenic Denicola <domenic at domenicdenicola.com> wrote:

I don't understand why this is happening. There was fairly strong consensus on symbols at the last meeting, and nothing new has been brought to the table. Why are people's opinions suddenly changing? Vague fearmongering about "complexity"? Symbols are a good solution to a real problem, much better than strings.

Agreed. Not having attended the meeting, it seems like everyone's suddenly gone crazy. ^_^

# David Herman (8 years ago)

On Sep 26, 2013, at 3:48 PM, Domenic Denicola <domenic at domenicdenicola.com> wrote:

I don't understand why this is happening. There was fairly strong consensus on symbols at the last meeting, and nothing new has been brought to the table. Why are people's opinions suddenly changing? Vague fearmongering about "complexity"? Symbols are a good solution to a real problem, much better than strings.

+so much

I am very disappointed by this thread.

# Domenic Denicola (8 years ago)

On Sep 26, 2013, at 18:49, "Yehuda Katz" <wycats at gmail.com> wrote:

Private state doesn't satisfy these requirements because they trap on the wrong side of the proxy.

Agreed, in many cases I don't want private state; I want something that can be copied by Object.mixin, for example. For private state, weak maps are fine, and used today already.

# Yehuda Katz (8 years ago)

On Thu, Sep 26, 2013 at 3:48 PM, Mark S. Miller <erights at google.com> wrote:

Once you add the registry, all these accidental collisions problems come back. If you don't add a registry, you lose the ability to duck type across realms.

If that's the case, we haven't thought the registry through well enough. You should get the cross-realm symbol through imports.

# David Herman (8 years ago)

On Sep 26, 2013, at 3:47 PM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

Oh, that's not Hungarian notation, it's just a name space qualifier.

Don't talk down -- Rick was making a metaphor.

But, if you assume that we will added a real private state mechanism into "ES 6.1" or "ES6.2" will Symbol really carry its weight looking back 10 years from now?

Hell yes, if it saved us from __iterator__ or std:iterator or @iterator or @[email protected]@[email protected]@iterator.

# Allen Wirfs-Brock (8 years ago)

On Sep 26, 2013, at 3:50 PM, David Herman wrote:

On Sep 26, 2013, at 3:48 PM, Domenic Denicola <domenic at domenicdenicola.com> wrote:

I don't understand why this is happening. There was fairly strong consensus on symbols at the last meeting, and nothing new has been brought to the table. Why are people's opinions suddenly changing? Vague fearmongering about "complexity"? Symbols are a good solution to a real problem, much better than strings.

+so much

I am very disappointed by this thread.

Actually, something new was brought to the table. The convention of using string literal specified, non-identifier property names for stratified meta operations.

It's enough to at least spend a few minutes to consider whether that is good enough to do for the primary use case (in the ES spec,) for Symbols.

# Yehuda Katz (8 years ago)

On Thu, Sep 26, 2013 at 3:57 PM, Allen Wirfs-Brock <allen at wirfs-brock.com>wrote:

Actually, something new was brought to the table. The convention of using string literal specified, non-identifier property names for stratified meta operations.

How is that new?

# Yehuda Katz (8 years ago)

Sorry, that was too short.

I don't understand what "using string literal specified, non-identifier property names" brings to the table to fundamentally alter the constraints that we've been working with that led to the consensus at the last f2f.

# Allen Wirfs-Brock (8 years ago)

On Sep 26, 2013, at 3:51 PM, Yehuda Katz wrote:

If that's the case, we haven't thought the registry through well enough. You should get the cross-realm symbol through imports.

Imports don't cover the case where you want to have different instances (and possible different versions) of the same library in different realms, but sill what to allow meta-level property interaction between the different realms.

For that you need some sort of registry.

# Rick Waldron (8 years ago)

On Thu, Sep 26, 2013 at 6:51 PM, Domenic Denicola < domenic at domenicdenicola.com> wrote:

On Sep 26, 2013, at 18:49, "Yehuda Katz" <wycats at gmail.com> wrote:

Private state doesn't satisfy these requirements because they trap on the wrong side of the proxy.

Agreed, in many cases I don't want private state; I want something that can be copied by Object.mixin, for example. For private state, weak maps are fine, and used today already.

Agreed with Yehuda and both of Domenic's points. I don't need/want Symbol for private anything—I want it for cases that don't deserve a WeakMap, should be reflected and can be "seen" by Object.getOwnPropertySymbols(), but want to avoid the possibility of collision.

# Mark S. Miller (8 years ago)

I think we swept the collision problem under the registry rug. Let's start by discussing registry designs assuming we do have Symbols. Let's see if we can design a registry that allows cross-realm duck typing without re-introducing the same possibility of accidental collision that we'd have without Symbols. Note: For this question, malicious collision is not an issue.

# Allen Wirfs-Brock (8 years ago)

On Sep 26, 2013, at 3:59 PM, Yehuda Katz wrote:

Sorry, that was too short.

I don't understand what "using string literal specified, non-identifier property names" brings to the table to fundamentally alter the constraints that we've been working with that led to the consensus at the last f2f.

The newness was using using string literals+ concise methods to write such meta=level methods.

What it brings to the table is that it address the meta stratification issue in a reasonable manner without having to add anything (other than the use of the hooks) to the ES5 level language. (and I'm ignoring the enumerability issues).

There is significant semantic complexity that Symbols add (I know, I just did all the work again). The possibility of avoiding that added complexity is worth a little bit of exploration.

# Brandon Benvie (8 years ago)

On 9/26/2013 4:09 PM, Allen Wirfs-Brock wrote:

The newness was using using string literals+ concise methods to write such meta=level methods.

What it brings to the table is that it address the meta stratification issue in a reasonable manner without having to add anything (other than the use of the hooks) to the ES5 level language. (and I'm ignoring the enumerability issues).

I don't see how any of the string key proposals so far are different from __proto__, which we agree is not an adequate level of stratification (none basically).

# Rick Waldron (8 years ago)

On Thu, Sep 26, 2013 at 6:57 PM, Allen Wirfs-Brock <allen at wirfs-brock.com>wrote:

Actually, something new was brought to the table. The convention of using string literal specified, non-identifier property names for stratified meta operations.

This will effectively "version" plain objects: one version that all code in existence was written to work with and the other that will become something developers regard as a "bad part" due to strange special behaviour that doesn't match expectations.

# Yehuda Katz (8 years ago)

I don't understand why any registry is needed. Imagine a module "ember/symbols" implemented like this, assuming some sharedSpace that modules have access to cross-realms.

var guid = "<generated literal>", symbol;

if (sharedSpace[guid]) {
  symbol = sharedSpace[guid];
} else {
  symbol = sharedSpace[guid] = new Symbol("friendly name");
}

export meta = symbol;

And then in any realm:

import { id } from "ember/symbols";

// use `id` symbol

So you still coordinate over Strings, but you don't need a VM-level registry, just a user-land one.

# David Herman (8 years ago)

So let's assume there is such a registry. You can avoid accidental collisions with the ugliest name in the universe -- gensym that thing to the n'th degree -- but you're no longer forcing all clients to deal with the ugliness. You've abstracted it behind your library. Let's say I'm implementing a serialization library and want to create a @@serialize symbol for people to coordinate on. I'd do something like the following:

// serialize.js
import { lookup, register } from "js/registry";

const myGUID = "[email protected]@blaaaa[email protected]@__";

export default = lookup(myGUID) || register(myGUID, new Symbol("friendly name"));

Now every client simply gets to use

import serialize from "serialize";

obj[serialize] = function() { ... };

without having to care about how ugly the GUID used in the registry was.

# Yehuda Katz (8 years ago)

On Thu, Sep 26, 2013 at 4:12 PM, Brandon Benvie <bbenvie at mozilla.com> wrote:

I don't see how any of the string key proposals so far are different from __proto__, which we agree is not an adequate level of stratification (none basically).

Indeed. The reason dunder doesn't work is that it's used in user-land due to the spec's usage, which means that it's not implausible that some library uses __iterator__ already for a different (or even similar) purpose.

The @ prefix dodged that bullet today (although it may introduce generated-code hazards), but it opens up exactly the same problem for the next time we need a new unique name.

# Rick Waldron (8 years ago)

On Thu, Sep 26, 2013 at 7:07 PM, Mark S. Miller <erights at google.com> wrote:

I think we swept the collision problem under the registry rug.

I'm not sure what you're trying to say, but if you mean that the collision problem is a non-issue, please explain because I disagree and have shown examples of collision earlier in this thread.

# Allen Wirfs-Brock (8 years ago)

On Sep 26, 2013, at 4:12 PM, Brandon Benvie wrote:

I don't see how any of the string key proposals so far are different from proto, which we agree is not an adequate level of stratification (none basically).

It moves the stratified names out of the syntactic space that JS programmers typically use for their own names. The Dunder names don't have that characteristics plus various sort of "_" prefixing is already used by many programmer at the application level.

# Mark S. Miller (8 years ago)

I am saying collision is an issue, but that it only seems that Symbols solve it because we haven't yet designed the registry. So we should do so first, and then re-examine this question in the context of a concrete registry design.

# Yehuda Katz (8 years ago)

On Thu, Sep 26, 2013 at 4:20 PM, Allen Wirfs-Brock <allen at wirfs-brock.com>wrote:

It moves the stratified names out of the syntactic space that JS programmers typically use for their own names. The Dunder names don't have that characteristics plus various sort of "_" prefixing is already used by many programmer at the application level.

Agreed, but this problem will come right back in ES7. Private names don't solve this issue because of where they trap, so we don't need a temporary patch, but a permanent solution.

# Rick Waldron (8 years ago)

On Thu, Sep 26, 2013 at 7:14 PM, David Herman <dherman at mozilla.com> wrote:

So let's assume there is such a registry. You can avoid accidental collisions with the ugliest name in the universe -- gensym that thing to the n'th degree -- but you're no longer forcing all clients to deal with the ugliness. You've abstracted it behind your library. Let's say I'm implementing a serialization library and want to create a @@serialize symbol for people to coordinate on. I'd do something like the following:

// serialize.js
import { lookup, register } from "js/registry";

const myGUID = "[email protected]@blaaaa[email protected]@__";

Thinking about this in terms of tooling, at even the terminal level, might look like this: ![{ '@@blaaaa[email protected]@': Function, '[symbol]': [Function] }

Subjectively, this makes my skin crawl.

# Allen Wirfs-Brock (8 years ago)

On Sep 26, 2013, at 4:13 PM, Yehuda Katz wrote:

I don't understand why any registry is needed. Imagine a module "ember/symbols" implemented like this, assuming some sharedSpace that modules have access to cross-realms.

var guid = "<generated literal>", symbol;
if (sharedSpace[guid]) {
  symbol = sharedSpace[guid];
} else {
  symbol = sharedSpace[guid] = new Symbol("friendly name");
}

export meta = symbol;

And then in any realm:

import { id } from "ember/symbols";

// use `id` symbol

So you still coordinate over Strings, but you don't need a VM-level registry, just a user-land one.

So the sharedSpace is a registry and it must be available to all realms that want to cooperate. How those that happen other than via help from the VM or the module load API (essentially part of the VM). Beyond that you are just using an external guid to give you names that won't unintentionally collide in the registry.

# Rick Waldron (8 years ago)

On Thu, Sep 26, 2013 at 7:22 PM, Mark S. Miller <erights at google.com> wrote:

I am saying collision is an issue, but that it only seems that Symbols solve it because we haven't yet designed the registry. So we should do so first, and then re-examine this question in the context of a concrete registry design.

Got it, thank you for clarifying.

# Brendan Eich (8 years ago)

Relax -- it's es-discuss.

I wrote "Among the no-symbol proposals", which you of course read correctly and knew excluded and allowed for a yes-symbol trump card. I still want symbols.

# David Herman (8 years ago)

On Sep 26, 2013, at 4:26 PM, Rick Waldron <waldron.rick at gmail.com> wrote:

Thinking about this in terms of tooling, at even the terminal level, might look like this: gyazo.com/f61d0e25366ce7e526c79ab7fa77cb17.png

No no, the GUID doesn't go in user land objects. It only goes in the registry. The user land objects only use the symbol. The GUID is just the entry into the registry, used internally by the serialize library.

IOW, I'm saying that symbols are better than obfuscated strings because even if you use obfuscated strings for managing the installation of globally shared symbols, those obfuscated strings are internal to the library and not exposed to client code or client objects. The client objects just use the clean symbol.

# Rick Waldron (8 years ago)

Thanks for the additional clarifying points :)

# David Herman (8 years ago)

On Sep 26, 2013, at 4:22 PM, Mark S. Miller <erights at google.com> wrote:

I am saying collision is an issue, but that it only seems that Symbols solve it because we haven't yet designed the registry. So we should do so first, and then re-examine this question in the context of a concrete registry design.

I don't think a registry design is necessary for symbols to be useful today.

But I agree it's a fine thing to consider for future-proofing. Design-wise, it would probably be cleanest to have a single function with a test-and-set semantics. Something like:

Symbol.register(stringKey[, friendlyName])

which registers in cross-realm shared state a new shared symbol registered under the given key, or returns the existing registered symbol if one already exists. This avoids any observable race conditions (except for competing friendlyNames, but that's pretty harmless) in the semantics but allows coordination across realms.

(Specification-wise, it's probably hard to specify the shared state precisely without having constructs like workers in the spec. But maybe it could be given an informal specification that speaks hand-wavily about being "thread-safe.")

# Allen Wirfs-Brock (8 years ago)

On Sep 26, 2013, at 4:30 PM, David Herman wrote:

On Sep 26, 2013, at 4:26 PM, Rick Waldron <waldron.rick at gmail.com> wrote:

Thinking about this in terms of tooling, at even the terminal level, might look like this: gyazo.com/f61d0e25366ce7e526c79ab7fa77cb17.png

No no, the GUID doesn't go in user land objects. It only goes in the registry. The user land objects only use the symbol. The GUID is just the entry into the registry, used internally by the serialize library.

Right, this is the first instance registers pattern. You still need to use something like a GUID or naming convention to avoid accidental registration collisions.

IOW, I'm saying that symbols are better than obfuscated strings because even if you use obfuscated strings for managing the installation of globally shared symbols, those obfuscated strings are internal to the library and not exposed to client code or client objects. The client objects just use the clean symbol.

But guid strings (whether externally or internally generated via a registry) could still be exported and used indirectly as property keys. This might be an argument for having computed property names in object literal and classes even if we decided Symbol wasn't needed.

# David Herman (8 years ago)

On Sep 26, 2013, at 4:43 PM, David Herman <dherman at mozilla.com> wrote:

On Sep 26, 2013, at 4:22 PM, Mark S. Miller <erights at google.com> wrote:

I am saying collision is an issue, but that it only seems that Symbols solve it because we haven't yet designed the registry. So we should do so first, and then re-examine this question in the context of a concrete registry design.

I don't think a registry design is necessary for symbols to be useful today.

But I agree it's a fine thing to consider for future-proofing.

And just to complete the point: see my earlier reply about how this allows you to abstract away the ugly GUID. It gives you the collision-avoidance and cross-realm shareability of ugly GUID's but without imposing the ugliness on every user of the symbol.

# David Herman (8 years ago)

On Sep 26, 2013, at 4:43 PM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

Right, this is the first instance registers pattern. You still need to use something like a GUID or naming convention to avoid accidental registration collisions.

Right, but the point is that you can abstract that away without it showing up with the gross GUID when you introspect on the object or view it in developer tools.

But guid strings (whether externally or internally generated via a registry) could still be exported and used indirectly as property keys.

I don't understand the relevance of this point. A GUID string is not === to a symbol that was registered with that GUID string.

# Allen Wirfs-Brock (8 years ago)

On Sep 26, 2013, at 4:43 PM, David Herman wrote:

On Sep 26, 2013, at 4:22 PM, Mark S. Miller <erights at google.com> wrote:

I am saying collision is an issue, but that it only seems that Symbols solve it because we haven't yet designed the registry. So we should do so first, and then re-examine this question in the context of a concrete registry design.

I don't think a registry design is necessary for symbols to be useful today.

But I agree it's a fine thing to consider for future-proofing. Design-wise, it would probably be cleanest to have a single function with a test-and-set semantics. Something like:

Symbol.register(stringKey[, friendlyName])

which registers in cross-realm shared state a new shared symbol registered under the given key, or returns the existing registered symbol if one already exists. This avoids any observable race conditions (except for competing friendlyNames, but that's pretty harmless) in the semantics but allows coordination across realms.

I assume that the "friendlyName" is just the non necessarily unique name that can be attached at a Symbol when it is created.

Agree with the test/and set style for registration, but I think there are also use cases for well known symbol access without registration. For example, it might be used for feature detection. So I'd suggest also having: Symbol.for(stringKey)

which returns either undefined or the Symbols that was already registered under that name.

(Specification-wise, it's probably hard to specify the shared state precisely without having constructs like workers in the spec. But maybe it could be given an informal specification that speaks hand-wavily about being "thread-safe.")

I wouldn't expect Symbols to be things that can cross serialization boundaries (such as between workers). You have to register them on the other side. This suggests that a serializer might to look to reverse map a symbol to the stringKey that was used to register it:

Symbol.keyFor(symbol)

returns either a string or undefined if that symbol has not been registered

# Allen Wirfs-Brock (8 years ago)

On Sep 26, 2013, at 4:51 PM, David Herman wrote:

On Sep 26, 2013, at 4:43 PM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

Right, this is the first instance registers pattern. You still need to use something like a GUID or naming convention to avoid accidental registration collisions.

Right, but the point is that you can abstract that away without it showing up with the gross GUID when you introspect on the object or view it in developer tools.

But guid strings (whether externally or internally generated via a registry) could still be exported and used indirectly as property keys.

I don't understand the relevance of this point. A GUID string is not === to a symbol that was registered with that GUID string.

I meant, if you didn't have symbols you could pretty much do the same thing by exporting a name that is bound to the GUID string as its value. That exported name could be imported by clients and used as the friendly way to refer to that property name, just like you are suggesting they do with Symbol values.

# David Herman (8 years ago)

On Sep 26, 2013, at 5:03 PM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

I meant, if you didn't have symbols you could pretty much do the same thing by exporting a name that is bound to the GUID string as its value. That exported name could be imported by clients and used as the friendly way to refer to that property name, just like you are suggesting they do with Symbol values.

The difference is the ergonomics. The GUID shows up in your developer tools, when you introspect on the property names, etc. The symbol shows up as a symbol, which is conceptually cleaner and vastly more readable. If you have 5 different GUIDs in your object, and you inspect the object in developer tools, you have to go and manually look up which one corresponds to which abstraction. Or if you use a human-readable but mildly obfuscated name, then you need a naming convention, and then you have the collision problem all over again. Finally, you can use an obfuscated GUID-y suffix but with a human-readable prefix, so at least humans have some hope of reading it, but you've still made your users' lives unpleasant.

With symbols you give all your users the pleasant experience of a clean distinction between symbol and string. And with some sort of registry, you can provide an abstraction that registers the symbol so that multiple realms can even coordinate on the symbol even in the face of multiple distinct copies of the library.

Am I not explaining this well? I feel like I've been trying to make this point several times over in this thread. One of the biggest issues with GUID's -- the thing that makes everyone turn three shades of green every time it gets proposed -- is the ergonomics. One of the main complaints people made about symbols was that it's not possible to do userland coordination across realms. While I don't think we have to solve that for ES6, my examples demonstrate that with a registry symbols absolutely can provide cross-realm coordination while tangibly beating out string conventions for ergonomics/usability/readability.

# Yehuda Katz (8 years ago)

The problem with the String proposals is that these things are not exactly Strings. Specifically, we all mostly agree that we want:

  • non-enumerability
  • throw on coercion to String
  • debugger-friendly names
  • some degree of non-collision
  • decent ergonomics, i.e. ability to easily understand that you're looking at a collision-resistant key and what it means
  • possibly other semantic distinctions that arise over time

It's possible to add these features to Strings (which is what we keep trying to do), especially with the aid of debugger tools, but it would mean shoehorning the semantics into a poorly-defined subset of Strings, and thus polluting the semantics of all Strings.

If we want something that's sort of like a String but with a bunch of semantic distinctions, let's make a new kind of thing.

# Mark S. Miller (8 years ago)

I think I understand everything on your list except "debugger friendly names". I agree that seeing a GUID in a debugger is unfriendly, and I understand why a debugger would not show a Symbol that way. But how would a debugger show a Symbol? Where does a Symbol's debugger-friendly name come from?

# Brendan Eich (8 years ago)

Mark S. Miller <mailto:erights at google.com> September 26, 2013 6:01 PM

I think I understand everything on your list except "debugger friendly names". I agree that seeing a GUID in a debugger is unfriendly, and I understand why a debugger would not show a Symbol that way. But how would a debugger show a Symbol? Where does a Symbol's debugger-friendly name come from?

The actual parameter to the Symbol constructor, converted to string.

# Allen Wirfs-Brock (8 years ago)

On Sep 26, 2013, at 5:22 PM, David Herman wrote:

Am I not explaining this well? I feel like I've been trying to make this point several times over in this thread.

probably in a earlier thread of this topic that I did't pay a lot of attention too.

One of the biggest issues with GUID's -- the thing that makes everyone turn three shades of green every time it gets proposed -- is the ergonomics. One of the main complaints people made about symbols was that it's not possible to do userland coordination across realms. While I don't think we have to solve that for ES6, my examples demonstrate that with a registry symbols absolutely can provide cross-realm coordination while tangibly beating out string conventions for ergonomics/usability/readability.

No the ergonomic issues are good arguments against using GUID for the the actual property keys. One of the attractions of the "@iterator" form is that it has pretty good ergonomics. If somebody wanted to use them but not not clutter the actual string with a name space qualifier (which may be a bit noisy, but is still a lot better than a GUID) they would have to use a Registry to access a friendly string property key.

# Kevin Smith (8 years ago)

Going to keep this short and sweet:

  • Enumerability? Yawn... Enumerability of user-created meta-level method names is fine and should be expected, just as we expect enumerability of "_"-prefixed method names today.

  • Duck typing must work across Realms. Symbols without a registry do not. You can make special cases for built-in symbols, but special cases make bad law.

  • If you are going to use a symbol registry, then you really need to prove how that is any better than just using the registry key itself.

  • Is getting a symbol with (1) a GUID and (2) a friendly name, then (3) toting that object around really more ergonomic than just using a string literal?

  • Perfect (collision avoidance) is the enemy of the good (collision avoidance).

  • Symbols are surprisingly tricky at the spec level. (new Symbol() throws, what?)

# Brendan Eich (8 years ago)

Kevin Smith <mailto:zenparsing at gmail.com> September 26, 2013 7:01 PM Going to keep this short and sweet:

  • Enumerability? Yawn... Enumerability of user-created meta-level method names is fine and should be expected, just as we expect enumerability of "_"-prefixed method names today.

The Object.extend examples are compelling. If we want extension via method-in-class syntax, we should care about enumerability. It will break Object.extend uses.

  • Duck typing must work across Realms. Symbols without a registry do not. You can make special cases for built-in symbols, but special cases make bad law.

(You learned from me.)

I agree world-of-realms matters, in many ways. We can solve this more generally, and should. I don't know the timing, but the idea that cross-realm local issues stop global progress via symbols is a bad trade in general. Must avoid getting stuck at local maximum.

  • If you are going to use a symbol registry, then you really need to prove how that is any better than just using the registry key itself.

GUID sucks, symbol that can't collide with any string rules. Nuff said!

  • Is getting a symbol with (1) a GUID and (2) a friendly name, then (3) toting that object around really more ergonomic than just using a string literal?

Maybe.

  • Perfect (collision avoidance) is the enemy of the good (collision avoidance).

This is the weakest link. Next!

  • Symbols are surprisingly tricky at the spec level. (new Symbol() throws, what?)

Allen should weigh in.

# Mark S. Miller (8 years ago)

On Thu, Sep 26, 2013 at 7:12 PM, Brendan Eich <brendan at mozilla.com> wrote:

I agree world-of-realms matters, in many ways. We can solve this more generally, and should. I don't know the timing, but the idea that cross-realm local issues stop global progress via symbols is a bad trade in general. Must avoid getting stuck at local maximum.

In the same spirit of brevity, you have this backwards. Local hill climbing with no lookahead is how to get stuck at a local maximum.

The lookahead needed here is not agreement on a registry, but at least a straw registry whose implications we understand. Perhaps we have one, which is fine. We should examine it as part of this discussion of Symbols.

# Brendan Eich (8 years ago)

Mark S. Miller <mailto:erights at google.com> September 26, 2013 7:45 PM

In the same spirit of brevity, you have this backwards. Local hill climbing with no lookahead is how to get stuck at a local maximum.

That's what I wrote!

We need a world-of-realms spec. If we have one, probably many problems become easy to solve. If we don't, then arguments against symbols in particular and any world-wide values (value objects) that lack lookahead prevail.

The lookahead needed here is not agreement on a registry, but at least a straw registry whose implications we understand. Perhaps we have one, which is fine. We should examine it as part of this discussion of Symbols.

Why do you assume a mutable, racy registry?

# Mark S. Miller (8 years ago)

On Thu, Sep 26, 2013 at 7:50 PM, Brendan Eich <brendan at mozilla.com> wrote:

That's what I wrote!

We need a world-of-realms spec. If we have one, probably many problems become easy to solve. If we don't, then arguments against symbols in particular and any world-wide values (value objects) that lack lookahead prevail.

Sorry, I misunderstood. In any case, yes.

Why do you assume a mutable, racy registry?

Care to propose a better registry?

(I agree that we should disqualify any registry that is also a global communications channel.)

# Mark S. Miller (8 years ago)

I think an adequate registry is exactly the two static methods Allen proposed:

Symbol.for(aString) ==> aSymbol

Symbol.keyFor(aSymbol) ==> aString

where for all strings S

Symbol.keyFor(Symbol.for(S)) === S

I think we've discussed this before -- it is effectively an interning table for Symbols. The nice thing about it is that it is not a global communications channel at all. There's no way to tell if a given string or symbol has already been registered. The interning table can be weak or not as the implementation prefers.

# Allen Wirfs-Brock (8 years ago)

Well, Symbol.keyFor(aSymbol) tells you whether or no aSymbol has been registered. Of course, you have to have either created the symbol or had it passed to you in order to even make that test. But jn the later case, would that make a 1-bit, one-time communications channel back to other holders of the same symbol

# Allen Wirfs-Brock (8 years ago)

On Sep 26, 2013, at 7:12 PM, Brendan Eich wrote:

Kevin Smith <mailto:zenparsing at gmail.com> September 26, 2013 7:01 PM

  • Symbols are surprisingly tricky at the spec level. (new Symbol() throws, what?)

Allen should weigh in.

The spec. works for the latest version from last weeks meeting is already done (other the reviews) so that's not an issue.

A bigger deal is that it touches a number of different places in the spec. so that is permanent added complexity relative not not having symbols at all.

# Mark Miller (8 years ago)

That's communicated information, not a communications channel. The passed Symbol itself conveys that information, just as a passed string conveys other information. The Symbol itself or the string must have arrived over a communications channel, but that's another matter. If Alice conveys a string or Symbol from Carol to Bob, she enables Carol to convey to Bob whatever information Carol pre-packages into those bits. But this doesn't enable further communication between Bob and Carol.

And the conveyance itself is only a one-way communication even of these pre-packaged bits. Carol has no ability to tell that Bob received it, even if Bob wishes to tell her.

# Mark Miller (8 years ago)

Imprecise wording. Corrected inline.

# David Herman (8 years ago)

On Sep 26, 2013, at 7:01 PM, Kevin Smith <zenparsing at gmail.com> wrote:

  • Enumerability? Yawn... Enumerability of user-created meta-level method names is fine and should be expected, just as we expect enumerability of "_"-prefixed method names today.

Whether you personally use it, for-in is a reality. Introspection of objects happens, so if you ship a library that's putting meta-level properties into objects it needs to make them non-enumerable to be robust in the face of client code that uses for-in but isn't prepared to understand the meta properties.

  • Duck typing must work across Realms. Symbols without a registry do not. You can make special cases for built-in symbols, but special cases make bad law.

Symbols are absolutely future-proof for a registry. If we don't solve all use cases today, we keep working to improve the language and address more use cases. Someone I know recently made the point that the perfect is the enemy of the good...

  • If you are going to use a symbol registry, then you really need to prove how that is any better than just using the registry key itself.

Between Yehuda and me I count 7 times that we've addressed this point in this thread.

esdiscuss/2013-September/033768, esdiscuss/2013-September/033770, esdiscuss/2013-September/033771, esdiscuss/2013-September/033774, esdiscuss/2013-September/033766, esdiscuss/2013-September/033756, esdiscuss/2013-September/033755

  • Is getting a symbol with (1) a GUID and (2) a friendly name, then (3) toting that object around really more ergonomic than just using a string literal?

Once again, read the above links. You aren't distinguishing between the ergonomics of the provider of a symbol and the consumer of a symbol, and it's the latter case that matters. The ergonomics are better for the consumer of a symbol than that of an obfuscated string, especially when working with developer tools or code that manipulates the key and ends up surfacing the obfuscated string.

  • Perfect (collision avoidance) is the enemy of the good (collision avoidance).

Being more likely to break isn't actually a virtue so I don't think there's really anything to respond to here.

  • Symbols are surprisingly tricky at the spec level. (new Symbol() throws, what?)

Come on now. Auto-wrapping of primitives for method calls is a well-understood part of JS, but the wrapped objects themselves are not particularly useful and can be an attractive nuisance.

And here I want to second Yehuda's point: we're talking about a different kind of concept than strings, so it deserves a different language feature. Excessive language parsimony just results in more complexity for the programmer. We've seen this movie before: arrays are just objects, integers are just doubles, environment records are just objects...

# Mark S. Miller (8 years ago)

Some other invariants:

For all strings S, Symbol.for(S) === Symbol.for(S)

For all Symbols R made by Symbol.for(S) for some string S

Symbol.keyFor(R) === Symbol.keyFor(R) // I think this can be derived from previous statements, but it is late and I'm tired.

For all Symbols U not made by Symbol.for(S) for any S

Symbol.keyFor(U) always throws.

Btw, R stands for Registered and U for Unregistered. A Symbol, once born unregistered, can never be registered. This perhaps plugs the communications channel Allen had in mind.

# Mark S. Miller (8 years ago)

That's fine for an unregistered Symbol, i.e., one created by calling the Symbol constructor. What about a registered Symbol, i.e., one created by calling Symbol.for?

# Erik Arvidsson (8 years ago)

What's the use case for Symbol.keyFor?

# Allen Wirfs-Brock (8 years ago)

On Sep 27, 2013, at 5:24 AM, Mark S. Miller wrote:

That's fine for an unregistered Symbol, i.e., one created by calling the Symbol constructor. What about a registered Symbol, i.e., one created by calling Symbol.for?

Perhaps, there could be a second optional parameter, Symbol.for(keyString, friendlyName)

However, that could be used as a way to determine if the key had already been registered So, if it is important to hide that information the registration key need to be used as the friendly name..

# Allen Wirfs-Brock (8 years ago)

On Sep 27, 2013, at 7:52 AM, Erik Arvidsson wrote:

What's the use case for Symbol.keyFor?

The use case was actually suggested in a response to dherman earlier in this thread when he mentioned sharing symbols between workers.

If you need to serialize an object with symbol keyed properties you need to convert the symbol into sometime that can be equivalently recreated on the other side of the serialization barrier. For a registered Symbol the obvious way to do this is via its registration name:

Symbol.toJSON = function() { let key = Symbol.keyFor(lthis); if (key === undefined) return null; //unregistered symbols can't be serialized in this example return "**Symbol:"+key; }

The deserializer would have to do a pass over the resulting objects replace=ing "**Symbol:..." property names with the locally register Symbol under the same key.

# Anne van Kesteren (8 years ago)

On Fri, Sep 27, 2013 at 11:28 AM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

The use case was actually suggested in a response to dherman earlier in this thread when he mentioned sharing symbols between workers.

We don't need to expose that to user-script though. That would be part of the structured cloning algorithm.

# Brendan Eich (8 years ago)

David Herman wrote:

We've seen this movie before: arrays are just objects, integers are just doubles, environment records are just objects...

Some of my biggest regrets! Top five list material (don't list the other two, please).

# Mark S. Miller (8 years ago)

The structured cloning algorithm is the last thing I want to use to communicate between workers. I advise avoiding it like the plague, instead serializing to JSON, sending the string, and unserializing on the other side. This also keeps the semantics of inter-worker communication aligned with the semantics of distributed inter-machine messaging.

# Mark S. Miller (8 years ago)

So a more polite answer is: communicating registered symbols between machines, with the same semantics as communicating them over a bit channel within the same address space. I can't use structured clone between machines.

# Anne van Kesteren (8 years ago)

On Fri, Sep 27, 2013 at 11:53 AM, Mark S. Miller <erights at google.com> wrote:

The structured cloning algorithm is the last thing I want to use to communicate between workers. I advise avoiding it like the plague, instead serializing to JSON, sending the string, and unserializing on the other side. This also keeps the semantics of inter-worker communication aligned with the semantics of distributed inter-machine messaging.

"Just use JSON" doesn't work for a large number of use cases — e.g. when dealing with ArrayBuffer, File objects, or rendering contexts — structured cloning does.

# Mark S. Miller (8 years ago)

Yeah, those also don't work between machines. So at least we have a clean conceptual split between use cases. My points apply to the case that wants to work uniformly with the inter-machine case.

# David Bruant (8 years ago)

Le 27/09/2013 17:57, Anne van Kesteren a écrit :

On Fri, Sep 27, 2013 at 11:53 AM, Mark S. Miller <erights at google.com> wrote:

The structured cloning algorithm is the last thing I want to use to communicate between workers. I advise avoiding it like the plague, instead serializing to JSON, sending the string, and unserializing on the other side. This also keeps the semantics of inter-worker communication aligned with the semantics of distributed inter-machine messaging. "Just use JSON" doesn't work for a large number of use cases — e.g. when dealing with ArrayBuffer, File objects, or rendering contexts — structured cloning does.

I can't speak for Mark, but I feel his advice is missing details. For instance, transferables are missing (covers ArrayBuffer and rendering contexts, though, I'd prefer parallel iframes to passing rendering contexts around). Not sure about File objects. Wouldn't they be better as transferables?

I agree with the intent of keeping semantics of inter-worker and inter-machine very close, but the difference in terms makes very clear that not being able to differenciate the 2 cases incurs an information loss that can be detrimental to some use cases, especially performance-related ones. Do transferables make sense in an inter-machine context?

# Anne van Kesteren (8 years ago)

On Fri, Sep 27, 2013 at 11:54 AM, Mark S. Miller <erights at google.com> wrote:

So a more polite answer is: communicating registered symbols between machines, with the same semantics as communicating them over a bit channel within the same address space. I can't use structured clone between machines.

That seems like a different use case than the one Allen brought up. Lots of things don't work between machines, why should Symbols?

# Anne van Kesteren (8 years ago)

On Fri, Sep 27, 2013 at 12:07 PM, David Bruant <bruant.d at gmail.com> wrote:

I can't speak for Mark, but I feel his advice is missing details. For instance, transferables are missing (covers ArrayBuffer and rendering contexts, though, I'd prefer parallel iframes to passing rendering contexts around). Not sure about File objects. Wouldn't they be better as transferables?

Structured cloning includes handling transferable objects.

# David Herman (8 years ago)

On Sep 27, 2013, at 9:07 AM, David Bruant <bruant.d at gmail.com> wrote:

I agree with the intent of keeping semantics of inter-worker and inter-machine very close, but the difference in terms makes very clear that not being able to differenciate the 2 cases incurs an information loss that can be detrimental to some use cases, especially performance-related ones.

Yeah, I worry about chasing after that holy grail of fully transparent distribution, which IMO is in the list of misbegotten CS fantasies, up there with fully automatic parallelization and human-level AI.

More to the point: as I see it, unique symbols are, like File objects and ArrayBuffers, an ephemeral construct that can only be managed within a single VM/computer. GUIDs are a fine thing to use for messaging protocols between computers in a distributed setting, but I see nothing wrong with an additional marshalling/unmarshalling layer being required to make that work. And I'm skeptical of such a layer being automatically managed by the language (probably for a similar reason to one of the things Mark hates about structured clone: the magic built-in behavior hard-wired to special built-in types).

That said, I could still imagine that if we had a solution for allowing handshaking between workers without them sharing state (which AIUI was Mark's issue with the registry strawman I suggested in this thread), that a similar mechanism could be useful for handshaking between computers: while user code would still manage the JSON encoding of symbolic names (e.g. by name mangling schemes), the unmarshalling side could then make sure to locally register the GUID with a symbol.

# David Herman (8 years ago)

On Sep 27, 2013, at 8:28 AM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

On Sep 27, 2013, at 7:52 AM, Erik Arvidsson wrote:

What's the use case for Symbol.keyFor?

The use case was actually suggested in a response to dherman earlier in this thread when he mentioned sharing symbols between workers.

If you need to serialize an object with symbol keyed properties you need to convert the symbol into sometime that can be equivalently recreated on the other side of the serialization barrier. For a registered Symbol the obvious way to do this is via its registration name:

Symbol.toJSON = function() { let key = Symbol.keyFor(lthis); if (key === undefined) return null; //unregistered symbols can't be serialized in this example return "**Symbol:"+key; }

The deserializer would have to do a pass over the resulting objects replace=ing "**Symbol:..." property names with the locally register Symbol under the same key.

Oh, I think I finally understand what you're getting at, and how it avoids any shared state. Let me summarize, and forgive me for just repeating what you've already said:

  • don't share symbols between workers
  • registries are per-worker (in which case there's absolutely nothing needed from the language mechanism-wise, except to establish conventions)
  • use some custom encoding for representing symbolic property names as string names that contain their registry key

Then workers can coordinate without needing to do any special handshaking or up-front message-passing, they just register names lazily as they encounter them in the marshalling/unmarshalling process.

AFAICT this constitutes a complete story for symbol registration that requires zero new mechanisms in the language (beyond symbols, of course).

# David Herman (8 years ago)

On Sep 27, 2013, at 10:02 AM, David Herman <dherman at mozilla.com> wrote:

  • don't share symbols between workers

Follow-up thought: it seems there are actually two concepts that both get grouped under "realms" and yet might warrant distinction. These correspond on the web to two same-origin windows vs two workers.

Between same-origin windows, you have different realms but a shared heap. In that case, you really want e.g. @@iterator to be === regardless of the realm it comes from. If one same-origin window shares an iterable object with another same-origin window, we want it to remain iterable. So we really want to specify that within a heap, multiple realms can share symbols (there's no way to stop them from sharing them anyway) and the ES6 standard symbols are unique. This also means that you want one registry shared across realms within that heap. I still don't think this needs new mechanism from the language, though, since same-origin windows can synchronously communicate to handshake on a shared registry object.

Between workers, you have different realms and completely separate heaps. Allen's suggestion has @@iterator being completely uncommunicable between the different heaps.

So I think Allen's protocol still works, you just have to have sharing of symbols across multiple realms that exist within the same worker/heap/vat/whatchamacallit.

# David Herman (8 years ago)

On Sep 27, 2013, at 10:14 AM, David Herman <dherman at mozilla.com> wrote:

On Sep 27, 2013, at 10:02 AM, David Herman <dherman at mozilla.com> wrote:

  • don't share symbols between workers

Follow-up thought: it seems there are actually two concepts that both get grouped under "realms" and yet might warrant distinction. These correspond on the web to two same-origin windows vs two workers.

Between same-origin windows, you have different realms but a shared heap. In that case, you really want e.g. @@iterator to be === regardless of the realm it comes from. If one same-origin window shares an iterable object with another same-origin window, we want it to remain iterable. So we really want to specify that within a heap, multiple realms can share symbols (there's no way to stop them from sharing them anyway) and the ES6 standard symbols are unique. This also means that you want one registry shared across realms within that heap. I still don't think this needs new mechanism from the language, though, since same-origin windows can synchronously communicate to handshake on a shared registry object.

Another follow-up: even easier than creating some new registry is just to use the module registry. You can do that when creating a new realm by simply sharing an existing module instance:

var newRealm = new Loader(...);
newRealm.set("serialize", System.get("serialize"));

Or if you're implementing a web library and you want to be sure that all windows see the same symbol the module can initialize its own symbols by querying the environment to see if the symbol already exists:

// serialize.js
let m = window.top.System.get("serialize");
export const serialize = m ? m.serialize : new Symbol("friendly name");
# David Herman (8 years ago)

On Sep 27, 2013, at 12:05 PM, David Herman <dherman at mozilla.com> wrote:

export const serialize = m ? m.serialize : new Symbol("friendly name");

And... of course I meant Symbol() rather than new Symbol(). Haven't retrained my muscle memory from the days of the object-based API. ;)

# Kevin Smith (8 years ago)

Whether you personally use it, for-in is a reality. Introspection of objects happens, so if you ship a library that's putting meta-level properties into objects it needs to make them non-enumerable to be robust in the face of client code that uses for-in but isn't prepared to understand the meta properties.

Is there a concrete example which shows how enumerability of meta-level properties would present a problem for such code? That might be convincing.

Once again, read the above links. You aren't distinguishing between the ergonomics of the provider of a symbol and the consumer of a symbol, and it's the latter case that matters. The ergonomics are better for the consumer of a symbol than that of an obfuscated string, especially when working with developer tools or code that manipulates the key and ends up surfacing the obfuscated string.

In re-reading the above links, you seem to be fighting a strawman. I don't propose using GUIDs at all. That would be awful. I propose using "@"-prefixed strings, whose ergonomics is clearly superior to the symbol-based solution.

Yes, prefixed strings don't give you a collision-free guarantee. Do we need that for meta hooks? Why?

Why would you even need to worry about a user-created object accidentally using a well-known "@"-prefixed string? Remember, I proposed that hooks are always functions, so the "JSON.parse" counter-argument doesn't apply.

# André Bargull (8 years ago)

/ Whether you personally use it, for-in is a reality. Introspection of />/ objects happens, so if you ship a library that's putting meta-level />/ properties into objects it needs to make them non-enumerable to be robust />/ in the face of client code that uses for-in but isn't prepared to />/ understand the meta properties. />/ />/ /Is there a concrete example which shows how enumerability of meta-level properties would present a problem for such code? That might be convincing.

There are all kinds for-in uses which don't expect meta-level hooks. For example this "isEmpty" function to test whether an object contains any entries:

function isEmpty(o) {
   for (var k in o) return false;
   return true;
}

In this case I doubt @iterator or @toStringTag should be visible. (And I know there a better ways to test for empty objects, but for-in testing is common enough...)

# Kevin Smith (8 years ago)

In this case I doubt @iterator or @toStringTag should be visible. (And I know there a better ways to test for empty objects, but for-in testing is common enough...)

Thanks Andre! I fear this example merely begs the question of whether such an object should be considered "empty" with respect to enumerability. Would anything break if the following object were considered "not empty"?

var obj = { "@iterator"() { /* ... */ } };

More generally, what is accomplished by hiding these hooks from for-in?

# Yehuda Katz (8 years ago)

On Fri, Sep 27, 2013 at 12:37 PM, Kevin Smith <zenparsing at gmail.com> wrote:

Whether you personally use it, for-in is a reality. Introspection of

objects happens, so if you ship a library that's putting meta-level properties into objects it needs to make them non-enumerable to be robust in the face of client code that uses for-in but isn't prepared to understand the meta properties.

Is there a concrete example which shows how enumerability of meta-level properties would present a problem for such code? That might be convincing.

Here are some examples:

  • Checking to see whether an object is empty. In Ember we do this, for example, to keep track of whether the list of changes to a model has become empty (and therefore to mark the object as clean). That's just one example.
  • Iterating over an object to print out a version for inspection. Meta-level properties (1) shouldn't be printed in such casual inspections, and (2) often contain recursive references that can cause infinite loops. Ember hit this issue with JSDump.
  • Iterating over an object in unit testing frameworks to compare for "deep equality". For example, you may want to compare whether some object is "deep equal" to { foo: bar }, but don't want to include Ember's meta-level properties that track observability.

In Ember, we mark our two meta-level properties as non-enumerable after hitting some of these issues.

It's probably possible to work around these issues carefully, but non-enumerability eliminated the entire class of problems triggered by interactions with third-party libraries.

Once again, read the above links. You aren't distinguishing between the ergonomics of the provider of a symbol and the consumer of a symbol, and it's the latter case that matters. The ergonomics are better for the consumer of a symbol than that of an obfuscated string, especially when working with developer tools or code that manipulates the key and ends up surfacing the obfuscated string.

In re-reading the above links, you seem to be fighting a strawman. I don't propose using GUIDs at all. That would be awful. I propose using "@"-prefixed strings, whose ergonomics is clearly superior to the symbol-based solution.

Yes, prefixed strings don't give you a collision-free guarantee. Do we need that for meta hooks? Why?

Once we start using @foo in ES, libraries will adopt the pattern (just as they did for dunder properties). Then we'll hit the same issue in ES7 when we want to add a new property. I've said this a few times in this thread.

Why would you even need to worry about a user-created object accidentally using a well-known "@"-prefixed string? Remember, I proposed that hooks are always functions, so the "JSON.parse" counter-argument doesn't apply.

The problem is code written in the ES6 era interacting with well-known "@"-prefixed Strings created in the ES7+ era (and the same for ES7, ES8+, etc.)

The "find a sequence of characters that is both ergonomic and not heavily in use" solution only works one time. If it's indeed ergonomic, it will be used in userland and then it will fail the "not heavily in use" requirement in the future.

# Anne van Kesteren (8 years ago)

On Thu, Sep 26, 2013 at 10:01 PM, Kevin Smith <zenparsing at gmail.com> wrote:

  • If you are going to use a symbol registry, then you really need to prove how that is any better than just using the registry key itself.

During the meeting I tried to make the point that symbols would be better for objects that have named getters, such as HTMLCollection:

<div [email protected]></div> <script>alert(document.getElementsByTagName("div")["@iterator"])</script>

Of course, web compatibility may or may not be impacted here and if not we may be able to make named getters more complicated by not looking at @-prefixed strings, but symbols have none of that. We could of course also not make HTMLCollection iterable, but that seems like a shame.

# Brendan Eich (8 years ago)

Kevin Smith wrote:

Is there a concrete example which shows how enumerability of meta-level properties would present a problem for such code? That might be convincing.

All the noise we made about Object.extend was unclear? From jQuery:

jquery/jquery/blob/master/src/core.js#L157

Many similar functions, going back to Prototype's Object.extend:

Object.extend = function(destination, source) {
   for (var property in source)
     destination[property] = source[property];
   return destination;
};
# André Bargull (8 years ago)

On 9/27/2013 9:58 PM, Kevin Smith wrote:

In this case I doubt @iterator or @toStringTag should be visible.
(And I know there a better ways to test for empty objects, but
for-in testing is common enough...)

Thanks Andre! I fear this example merely begs the question of whether such an object should be considered "empty" with respect to enumerability. Would anything break if the following object were considered "not empty"?

 var obj = { "@iterator"() { /* ... */ } };

More generally, what is accomplished by hiding these hooks from for-in?

Whether or not some code breaks depends on how that function is used, so it's hard for me to give a general answer at this point. Concerning your second question about the benefits of hiding hooks: I'd assume if something is a "meta-level" hook, it should only be visible on the "meta-level" and not on the concrete data level. And for-in is rather a tool to access the concrete data level than to access the meta-level.

PS: The function is called "isEmptyObject" in jQuery ->

jquery/jquery/blob/master/src/core.js#L267-L273

  • André
# Waldemar Horwat (8 years ago)

On 09/26/2013 04:22 PM, Yehuda Katz wrote:

Agreed, but this problem will come right back in ES7. Private names don't solve this issue because of where they trap, so we don't need a temporary patch, but a permanent solution.

It's even back in ES6 with "then". I find it truly weird that we're trying to use two different mechanisms to get the <then> and <iterate> metaproperties. We should be cutting down the complexity, not adding to it in a manner which the unknowns bouncing around this thread indicate is reckless.

# Brendan Eich (8 years ago)

Waldemar Horwat <mailto:waldemar at google.com> September 27, 2013 3:45 PM

It's even back in ES6 with "then". I find it truly weird that we're trying to use two different mechanisms to get the <then> and <iterate> metaproperties. We should be cutting down the complexity, not adding to it in a manner which the unknowns bouncing around this thread indicate is reckless.

You have a point. Indeed Jason Orendorff urged us to use 'iterator', not an @@iterator symbol, to name the unstratified trap for the iteration protocol. But he lost, since we are in new territory and have a chance to avoid collisions.

Not so with promises, which want 'then' to name the duck-typed method. That's an API cowpath the cows have already trod, indeed almost paved (with what topping, I won't say ;-).

# Kevin Smith (8 years ago)

All the noise we made about Object.extend was unclear? From jQuery:

jquery/**jquery/blob/master/src/core.**js#L157jquery/jquery/blob/master/src/core.js#L157

Many similar functions, going back to Prototype's Object.extend:

Object.extend = function(destination, source) { for (var property in source) destination[property] = source[property]; return destination; };

Thanks. I believe this supports my position. Regardless of how @iterator is defined, consider this:

extendObject(target, someObjectWithIterator);

If someObjectWithIterator has an @iterator method (be it string or symbol keyed), I would expect that after this statement runs, target would also have that @iterator method. To write it a different way:

extendObject(target, {
  "@iterator"() {}
});

Or:

extendObject(target, {
  [Symbol.iterator]() {}
});

Either way I would be surprised if the "iterator" function was not copied. I think this example argues for enumerability, at least in some cases. Or equivalently, not all meta-level hooks appear to want non-enumerability.

# Kevin Smith (8 years ago)

<div [email protected]></div> <script>alert(document.getElementsByTagName("div")["@iterator"])</script>

This is a good point, and one which I was trying to reason about (way) upthread. This might do it - have to sleep on it, though...

# Brendan Eich (8 years ago)

Kevin Smith <mailto:zenparsing at gmail.com> September 27, 2013 9:44 PM

All the noise we made about Object.extend was unclear? From jQuery:

https://github.com/jquery/jquery/blob/master/src/core.js#L157

Many similar functions, going back to Prototype's Object.extend:

Object.extend = function(destination, source) {
  for (var property in source)
    destination[property] = source[property];
  return destination;
};

Thanks. I believe this supports my position.

Make your case first!

Regardless of how @iterator is defined, consider this:

extendObject(target, someObjectWithIterator);

If someObjectWithIterator has an @iterator method (be it string or symbol keyed), I would expect that after this statement runs, target would also have that @iterator method.

Any Prototype or jQuery user would be surprised to have meta-level additions show up, possibly breaking extant code. We can't reason a priori about such extensions and their effects on backward compatibility and say "always good" or "always bad".

Furthermore, Object.extend's second argument is almost universally an object literal. No issue in that common case, but exceptional cases express the second argument differently. If it happens to be an array, while the first is not an array, who knows what's "right"? What the author intended was what happend in the older engines that don't have the extension.

To write it a different way:

extendObject(target, {
  "@iterator"() {}
});

Or:

extendObject(target, {
  [Symbol.iterator]() {}
});

Either way I would be surprised if the "iterator" function was not copied.

The final challenge for existing libraries that use for-in loop: if we add symbols to JS, they will violate the always-stringified enumerable property key definition of for-in.

I think this example argues for enumerability, at least in some cases.

AHA! :-P

We can agree on "at least in some cases". The problem is the language extension cannot distinguish the cases and "do the right thing" always. Your proposal keeps identifiers string-equated, so any enumerable property with such an "@..." name will be visited by for-in, of course. With symbols, to avoid surprising existing for-in-based code, we must go the other way and provide Object.getOwnPropertySymbols as in ES6's latest draft.

Or equivalently, not all meta-level hooks appear to want non-enumerability.

Even for @iterator, it depends. The array Object.extend source with non-array target is one possibility.

# Brendan Eich (8 years ago)

Kevin Smith <mailto:zenparsing at gmail.com> September 27, 2013 9:56 PM

<div [email protected]></div>
<script>alert(document.getElementsByTagName("div")["@iterator"])</script>

This is a good point, and one which I was trying to reason about (way) upthread. This might do it - have to sleep on it, though...

I took Anne's cheeky lack of quotes around the div's id attribute to be just good HTML minimal style :-P. No extension there.

In other words, I thought this was an argument against using "@iterator" to name the unstratified iteration protocol trap.

# Allen Wirfs-Brock (8 years ago)

On Sep 27, 2013, at 9:44 PM, Kevin Smith wrote:

All the noise we made about Object.extend was unclear? From jQuery:

jquery/jquery/blob/master/src/core.js#L157

Many similar functions, going back to Prototype's Object.extend:

Object.extend = function(destination, source) { for (var property in source) destination[property] = source[property]; return destination; };

Thanks. I believe this supports my position. Regardless of how @iterator is defined, consider this:

extendObject(target, someObjectWithIterator);

If someObjectWithIterator has an @iterator method (be it string or symbol keyed), I would expect that after this statement runs, target would also have that @iterator method. To write it a different way:

extendObject(target, {
  "@iterator"() {}
});

Or:

extendObject(target, {
  [Symbol.iterator]() {}
});

Either way I would be surprised if the "iterator" function was not copied. I think this example argues for enumerability, at least in some cases. Or equivalently, not all meta-level hooks appear to want non-enumerability.

{ Kevin }

Note that ES6 provides two built-in functions that provide Object.extend-like functionality. One that uses enumerability and assignment semantics and one that ignores enumerability and uses property definition semantics:

Object.assign people.mozilla.org/~jorendorff/es6-draft.html#sec-19.1.3.1

Object.mixin people.mozilla.org/~jorendorff/es6-draft.html#sec-19.1.3.15

When people start using those, they will be expressing an implicit intent.

It is for legacy libraries such as jquery Object.extend where the intent regarding meta=level properties is unclear.

Allens

# Kevin Smith (8 years ago)

I took Anne's cheeky lack of quotes around the div's id attribute to be just good HTML minimal style :-P. No extension there.

In other words, I thought this was an argument against using "@iterator" to name the unstratified iteration protocol trap.

Yes, it is - and it might be the counter-argument that torpedoes my proposal. Maybe...

# Kevin Smith (8 years ago)

Anne,

Would you agree that HTMLCollection is an inherently future-hostile API? That is, it is impossible to add any methods or properties to the API without potentially breaking compatibility?

# Anne van Kesteren (8 years ago)

On Sat, Sep 28, 2013 at 10:14 PM, Kevin Smith <zenparsing at gmail.com> wrote:

Would you agree that HTMLCollection is an inherently future-hostile API? That is, it is impossible to add any methods or properties to the API without potentially breaking compatibility?

Right, which is why symbols are so nice as HTMLCollection gets around.

# Brendan Eich (8 years ago)

Anne van Kesteren <mailto:annevk at annevk.nl> September 30, 2013 6:27 AM

Right, which is why symbols are so nice as HTMLCollection gets around.

Yes, for naming meta-level stuff.

For naming methods, not nice. This limits future additions.

# Anne van Kesteren (8 years ago)

On Mon, Sep 30, 2013 at 12:12 PM, Brendan Eich <brendan at mozilla.com> wrote:

Yes, for naming meta-level stuff.

For naming methods, not nice. This limits future additions.

I think all we want here is make HTMLCollection interoperate better with other lists. For new features we moved away from HTMLCollection for the reason you mention.

# Kevin Smith (8 years ago)

I think all we want here is make HTMLCollection interoperate better with other lists. For new features we moved away from HTMLCollection for the reason you mention.

What do you think an HTMLCollection @@iterate method should do? Iterate like an array, or like a map?

# Rick Waldron (8 years ago)

On Mon, Sep 30, 2013 at 12:54 PM, Kevin Smith <zenparsing at gmail.com> wrote:

I think all we want here is make HTMLCollection interoperate better with other lists. For new features we moved away from HTMLCollection for the reason you mention.

What do you think an HTMLCollection @@iterate method should do? Iterate like an array, or like a map?

In Firefox (regular and Nightly), HTMLCollection objects are iterable (with a the temporary placeholder "iterator" method):

var collection = document.body.children;
for (var child of collection) {
  console.log(child);
}

This matches my expectation, which is "like an array" in that HTMLCollection instances are iterables.

# Boris Zbarsky (8 years ago)

On 9/30/13 12:54 PM, Kevin Smith wrote:

What do you think an HTMLCollection @@iterate method should do? Iterate like an array, or like a map?

Probably like an array.

Note that the set of properties iterated by for/in on HTMLCollection is ... <sigh>. But I don't think that should constrain what for/of does, hopefully?

# Brendan Eich (8 years ago)

Boris Zbarsky wrote:

Note that the set of properties iterated by for/in on HTMLCollection is ... <sigh>. But I don't think that should constrain what for/of does, hopefully?

Not constrained at all, by design.

The new for-of forms trigger the iteration protocol. for-in is what it is (and should be specified better for interop in ES7).

# David Bruant (8 years ago)

Le 30/09/2013 19:30, Rick Waldron a écrit :

In Firefox (regular and Nightly), HTMLCollection objects are iterable (with a the temporary placeholder "iterator" method):

drive-by comment to say that "iterator" is going to be changed to "@@iterator" (the string) until symbols are implemented at which point a symbol will be used. Progress can be seen at bugzilla.mozilla.org/show_bug.cgi?id=907077 (which I had filed for a completely different purpose but whatever)

# Oliver Hunt (8 years ago)

Shipping JSC supports Symbols (although by the old name, Name, alas)

—Oliver