Symbols, Protocols, Frames, and Versioning
Thanks for pointing this out. Python's dunder-prefixing or anything like it in JS has that advantage: you can spell the magic property name with a string that works in any frame or global object. Of course strings can collide.
Symbols are useful in spite of this, but it is telling that we want @iterator to be a singleton across all potentially connected frames.
So should there be a way in the language to create singleton symbols? If so, how?
Le 03/10/2012 21:01, Brendan Eich a écrit :
Thanks for pointing this out. Python's dunder-prefixing or anything like it in JS has that advantage: you can spell the magic property name with a string that works in any frame or global object. Of course strings can collide.
Symbols are useful in spite of this, but it is telling that we want @iterator to be a singleton across all potentially connected frames.
So should there be a way in the language to create singleton symbols? If so, how?
Support from the system is required since it can't be achieved by JS scripts themselves. System modules, maybe?
Would it suffice to allow cross-frame sharing of symbols via postMessage and its structured clone algorithm? They're immutable, right?
So should there be a way in the language to create singleton symbols? If so, how?
new Symbol("21fef4ae-1439-4b6a-b412-3585906b35f1");
: ) Joking, of course.
Seriously though, in order to specify a singleton symbol in user code, you'd have to give the system enough information to be able to identify other occurrences of such a symbol in the multi-global environment. So in effect, you'd have to supply the system with a globally unique identifier. Ergo, we've travelled right back around to maintaining a global namespace. "org.ecmascript.system.iterator", anyone? ; )
You could say that the URL of the script where the symbol is defined forms the basis of such a unique identifier, but we'd still have to worry about multiple copies of the same script (or perhaps different versions) located at different URLs.
A generic solution for cross-frame sharing would most elegant (instanceof...). But how?
Not necessarily: you could have an environment that is global to all frames. But that seems like a security risk.
On Oct 3, 2012 12:39 PM, "Domenic Denicola" <domenic at domenicdenicola.com>
wrote:
Would it suffice to allow cross-frame sharing of symbols via postMessage and its structured clone algorithm? They're immutable, right?
I'm trying to follow this thread, but I'm having trouble understanding the problem. There are two cross frame cases: 1 same domain - objects can be used across frames 2 cross domain - post message only These cases have no practical overlap. Since you can't use objects across domains their type comparisons are not relevant. And when you do use objects across frames, then you want -- but don't have -- one global space for built-ins. I think developers want Array to be the same object in both frames but Array instances of the same name to be distinct, as if the iframe Array was pre-loaded with window.parent.Array.
Thus using postMessage in the same domain case doesn't make sense to me. (And if I'm completely missing the subject my apology).
jjb
Domenic Denicola wrote:
Would it suffice to allow cross-frame sharing of symbols via postMessage and its structured clone algorithm? They're immutable, right?
They are immutable but you'd still have to pass your @iterator to another same-origin frame, and then have to use it carefully when iterating objects from the first frame. This is unusable.
Making @iterator a singleton (to the limits of observability: same-origin, CORS, out-of-process-via-DOM window.open in IE9+ notwithstanding!) can be done. That wins, no need to pass and use the other frame's @iterator symbol.
But how to let users create such singletons?
Le 03/10/2012 23:09, Brendan Eich a écrit :
Domenic Denicola wrote:
Would it suffice to allow cross-frame sharing of symbols via postMessage and its structured clone algorithm? They're immutable, right?
They are immutable but you'd still have to pass your @iterator to another same-origin frame, and then have to use it carefully when iterating objects from the first frame. This is unusable.
Making @iterator a singleton (to the limits of observability: same-origin, CORS, out-of-process-via-DOM window.open in IE9+ notwithstanding!) can be done. That wins, no need to pass and use the other frame's @iterator symbol.
But how to let users create such singletons?
I concur with Kevin's analysis. Global namespace or it's not possible. The problem to be solved is to have 2 independent ECMAScript environments "creating" (or rather retrieving) the same unforgeable item. It's just not possible. If you create something in an ECMAScript runtime it's initially fully local, there is no way another runtime can know about it without providing authority that could be otherwise abused.
Unforgeability can be given up, but you end up with global namespaces. new Symbol("21fef4ae-1439-4b6a-b412-3585906b35f1"); or "org.ecmascript.system.iterator"
I've faced an equivalent problem recently, so I wish to take this occasion to share an idea on how to fix the awful security policy of local storage. An alternative design would be that instead of defaulting to Same-Origin Policy, we'd say that storages are only available initially to those who create it and who the creator shared it with.
var s = new Storage();
s.secret; // serializable identifier
// send the identifier to anywho is trusted like another frame or a
server
// in another frame/tab/window (of the same browser obviously)
var s = Storage.get(secret);
// same storage regardless of the origin
Trust domain is no longer "Same-Origin" but rather "whoever knows the secret id regardless of the origin". The secret can even be hidden from same-origin pages. Useful when webservers hosts content from different people; at my school, people had www.enseirb-matmeca.fr/~bruant addresses. Creating one page, I could have stolen the local storage of my school domain anytime. It makes the feature basically unusable for such cases. It's the global identifier pattern. Local storage secrets needs to transfered from server to client to recover the storage securely. Of course, the secret needs to be sent over HTTPS :-)
The private symbol has been replaced by a storage instance, but it's the same problem, I think.
For that matter, I've started a prototype implementation [1]. I have all ideas straight, but the prototype isn't functional yet. I need to hack on DNS (which I know nothing about); if anyone is interested in helping on that, I'll be happy of the help.
David
David Bruant wrote:
Unforgeability can be given up, but you end up with global namespaces. new Symbol("21fef4ae-1439-4b6a-b412-3585906b35f1"); or "org.ecmascript.system.iterator"
This is no better than dunder-iterator (I mean 'iterator', I just like typing dunder- ;-), or just 'iterator' (what Firefox uses currently).
I've faced an equivalent problem recently, so I wish to take this occasion to share an idea on how to fix the awful security policy of local storage. An alternative design would be that instead of defaulting to Same-Origin Policy, we'd say that storages are only available initially to those who create it and who the creator shared it with.
Ocap, yay! [sincere here]
var s = new Storage(); s.secret; // serializable identifier // send the identifier to anywho is trusted like another frame or a
server
// in another frame/tab/window (of the same browser obviously) var s = Storage.get(secret); // same storage regardless of the origin
Trust domain is no longer "Same-Origin" but rather "whoever knows the secret id regardless of the origin". The secret can even be hidden from same-origin pages. Useful when webservers hosts content from different people; at my school, people had www.enseirb-matmeca.fr/~bruant addresses. Creating one page, I could have stolen the local storage of my school domain anytime.
Yes, old prob with same-origin. Remember jwz.livejournal.com? The fix costs in subdomains.
BTW I am speaking on "The Same-Origin Saga" at AppSecUSA 2012 in Austin on the 26th:
schedule.appsecusa.org/event/6c4dcf0d7b78539d5eab850243fbb668#.UGzS2Rjvx1I
I'll have a look at your Extorage thing when able, thanks for the link.
On Wed, Oct 3, 2012 at 5:09 PM, Brendan Eich <brendan at mozilla.org> wrote:
Domenic Denicola wrote:
Would it suffice to allow cross-frame sharing of symbols via postMessage and its structured clone algorithm? They're immutable, right?
They are immutable but you'd still have to pass your @iterator to another same-origin frame, and then have to use it carefully when iterating objects from the first frame. This is unusable.
Making @iterator a singleton (to the limits of observability: same-origin, CORS, out-of-process-via-DOM window.open in IE9+ notwithstanding!) can be done. That wins, no need to pass and use the other frame's @iterator symbol.
But how to let users create such singletons?
The module system does this for us, doesn't it? I can't really see the problem -- anywhere you can share objects with private symbols you can always provide the symbols themselves. Module sandboxes will come in handy.
On Oct 3, 2012, at 2:09 PM, Brendan Eich wrote:
Domenic Denicola wrote:
Would it suffice to allow cross-frame sharing of symbols via postMessage and its structured clone algorithm? They're immutable, right?
They are immutable but you'd still have to pass your @iterator to another same-origin frame, and then have to use it carefully when iterating objects from the first frame. This is unusable.
Making @iterator a singleton (to the limits of observability: same-origin, CORS, out-of-process-via-DOM window.open in IE9+ notwithstanding!) can be done. That wins, no need to pass and use the other frame's @iterator symbol.
But how to let users create such singletons?
It isn't an issue about creating them, it's an issue about communicating them.
This issue came up briefly yesterday on the "prototypes as valid instances" thread: esdiscuss/2012-October/025453 where it was brought up in the context of cross-frame branding of the internal state of self-hosted built-ins.
Regarding @@iterator, @@toStringTag and any other specification defined unique symbols, my intent is that there will be spec language that says that there is a single value for each of them that is used across all Realms/contexts/frames (whatever terminology ultimately sticks). Note that this is not necessarily the case for all private symbols that might be used by the spec. or an implementation to reference private state of built-ins. Such symbols might need to be uniquely instantiated for each Realm/context/frame, it depends upon the actual design of the abstraction that is being created.
If a symbol is going to be used as an access key, then there must be a means of distributing it to its intended users. For built-in symbols like @@iterator this is probably going to be via an export from from a standard module. Presumably a similar technique could be used for user defined symbols that need to be shared across frame but the needs to be a way to communicate the actual shared symbol values to the module instance associated with each realm. In the above referenced message I suggest that this may be a job for the module loader API, but I haven't tried to work out the details.
Dean Landolt wrote:
On Wed, Oct 3, 2012 at 5:09 PM, Brendan Eich <brendan at mozilla.org <mailto:brendan at mozilla.org>> wrote:
Domenic Denicola wrote: Would it suffice to allow cross-frame sharing of symbols via postMessage and its structured clone algorithm? They're immutable, right? They are immutable but you'd still have to pass your @iterator to another same-origin frame, and then have to use it carefully when iterating objects from the first frame. This is unusable. Making @iterator a singleton (to the limits of observability: same-origin, CORS, out-of-process-via-DOM window.open in IE9+ notwithstanding!) can be done. That wins, no need to pass and use the other frame's @iterator symbol. But how to let users create such singletons?
The module system does this for us, doesn't it? I can't really see the problem -- anywhere you can share objects with private symbols you can always provide the symbols themselves. Module sandboxes will come in handy.
You're clearly right for built-ins. For a built-in module, say "@iter" (if we factor it that way), all realms importing @iterator from "@iter" will get the same singleton symbol.
In any user-defined symbol setting, it's up to the usercode to export the symbol from a module. But I don't believe we have spec'ed how the default loader works in a multiple-globals (multiple realms, it's catchy!) situation where the different globals are connected. Does the system loader memoize per reachable subgraph of global objects?
Allen Wirfs-Brock wrote:
Presumably a similar technique could be used for user defined symbols that need to be shared across frame but the needs to be a way to communicate the actual shared symbol values to the module instance associated with each realm. In the above referenced message I suggest that this may be a job for the module loader API, but I haven't tried to work out the details.
I don't think we've had a proposal yet for singleton modules per set of connected realms. Possibly I missed it (something is tickling a memory but I can't find a ref). It could be good!
Using a custom loader is always an option, but heavyweight.
2012/10/4 Brendan Eich <brendan at mozilla.org>
I don't think we've had a proposal yet for singleton modules per set of connected realms. Possibly I missed it (something is tickling a memory but I can't find a ref). It could be good!
This might have been the post that tickled your memory: esdiscuss/2012-September/024946
But this was about singleton module instances per loader context. Not sure if "loader context" coincides with "set of connected realms" though.
2012/10/4 Brendan Eich <brendan at mozilla.org>
David Bruant wrote:
Unforgeability can be given up, but you end up with global namespaces. new Symbol("21fef4ae-1439-4b6a-**b412-3585906b35f1"); or "org.ecmascript.system.**iterator"
This is no better than dunder-iterator (I mean 'iterator', I just like typing dunder- ;-), or just 'iterator' (what Firefox uses currently).
I agree it's the exact same thing.
I've faced an equivalent problem recently, so I wish to take this
occasion to share an idea on how to fix the awful security policy of local storage. An alternative design would be that instead of defaulting to Same-Origin Policy, we'd say that storages are only available initially to those who create it and who the creator shared it with.
Ocap, yay! [sincere here]
Caught. The discovery of Ocaps and more generally POLA (Principle Of Least Authority) fundamentally changed the way I reason about programming especially when it comes to security.
var s = new Storage();
s.secret; // serializable identifier // send the identifier to anywho is trusted like another frame or a
server
// in another frame/tab/window (of the same browser obviously) var s = Storage.get(secret); // same storage regardless of the origin
Trust domain is no longer "Same-Origin" but rather "whoever knows the secret id regardless of the origin". The secret can even be hidden from same-origin pages. Useful when webservers hosts content from different people; at my school, people had www.enseirb-matmeca.fr/**~bruantwww.enseirb-matmeca.fr/~bruantaddresses. Creating one page, I could have stolen the local storage of my school domain anytime.
Yes, old prob with same-origin. Remember jwz.livejournal.com? The fix costs in subdomains.
Actually, that's the biggest problem I have with the local storage spec. It basically tells you how to organize your URLs. That's an absurd demand from a feature which "only" aims at storing data in the user agent. Hopefully, no other spec imposes such a constraint. Hopefully, no other spec imposes contradictory constraints about URLs...
Tom Van Cutsem wrote:
2012/10/4 Brendan Eich <brendan at mozilla.org <mailto:brendan at mozilla.org>>
I don't think we've had a proposal yet for singleton modules per set of connected realms. Possibly I missed it (something is tickling a memory but I can't find a ref). It could be good!
This might have been the post that tickled your memory: esdiscuss/2012-September/024946
Yes, thanks.
But this was about singleton module instances per loader context. Not sure if "loader context" coincides with "set of connected realms" though.
AFAIK there's no facility for loading or finding already loaded a singleton module per connected set of realms.
If there were, it could be good for public symbol usage cross-realm as Kevin points out. It could also make a nasty channel among realms (IIRC the navigator object in some browsers can act like that, or perhaps only within a navigation history of one realm with multiple inner window objects and one WindowProxy public global).
Dave Herman mentioned another idea: intern'ing strings as symbols. You'd lose uniqueness but avoid colliding with any string. So
public @iterator = Symbol.intern('iterator');
Is this much better than just using 'iterator' or dunder-iterator?
Dave Herman mentioned another idea: intern'ing strings as symbols. You'd lose uniqueness but avoid colliding with any string. So
public @iterator = Symbol.intern('iterator');
Is this much better than just using 'iterator' or dunder-iterator?
Apologies, but what would interning a string as a symbol mean?
Kevin Smith wrote:
Dave Herman mentioned another idea: intern'ing strings as symbols. You'd lose uniqueness but avoid colliding with any string. So public @iterator = Symbol.intern('iterator'); Is this much better than just using 'iterator' or dunder-iterator?
Apologies, but what would interning a string as a symbol mean?
Different frames could load a module that does this and the symbol would be interned as a singleton in all connected realms.
The rendezvous would be on the name, so of course collisions are possible. Symbol.intern('org.ecma-international.es6.iterator') -- blech!
As noted, this loses symbol uniqueness but avoids string vs. string name collisions in objects. It takes on string-name collisions in the intern'ed symbol table of course. That may be more manageable. Or not!
presumably, looking up "iterator" in a registry of string/symbol key value pairs. Creating a new entry if one isn't present, and regardless returning the symbol value associated with the string.
public @iterator = Symbol('iterator');
conceptually could just as easily be expressed as:
public @iterator = RegistryOfWellKnownSymbols.lookup('iterator');
Allen Wirfs-Brock wrote:
presumably, looking up "iterator" in a registry of string/symbol key value pairs. Creating a new entry if one isn't present, and regardless returning the symbol value associated with the string.
public @iterator = Symbol('iterator');
No, I wrote Symbol.intern('iterator') on purpose. What you wrote just makes a unique symbol with debugging/diagnostics string associated with it.
conceptually could just as easily be expressed as:
public @iterator = RegistryOfWellKnownSymbols.lookup('iterator');
Yes, Symbol.intern would be just like that -- but shorter and standard if we choose to do it.
On Oct 4, 2012, at 10:09 AM, Brendan Eich wrote:
Allen Wirfs-Brock wrote:
presumably, looking up "iterator" in a registry of string/symbol key value pairs. Creating a new entry if one isn't present, and regardless returning the symbol value associated with the string.
public @iterator = Symbol('iterator');
No, I wrote Symbol.intern('iterator') on purpose. What you wrote just makes a unique symbol with debugging/diagnostics string associated with it.
oops, typo on my part, i meant intern.
On Thu, Oct 4, 2012 at 8:27 AM, Brendan Eich <brendan at mozilla.com> wrote:
Dave Herman mentioned another idea: intern'ing strings as symbols. You'd lose uniqueness but avoid colliding with any string. So
public @iterator = Symbol.intern('iterator');
Is this much better than just using 'iterator' or dunder-iterator?
I like it!
Yes, I think it's better than just using 'iterator'. Pushing these things into a separate namespace, as this effectively does, avoids most of the accidental collisions. It also lets you do collision-avoidance via ad hoc namespacing without paying the long-name tax more than once (when you first create the Symbol).
It might be useful to expose this functionality with a more obvious name, to underscore that you lose the secrecy/unforgability. Symbol.public()?
Tab Atkins Jr. wrote:
It might be useful to expose this functionality with a more obvious name, to underscore that you lose the secrecy/unforgability. Symbol.public()?
We are mooting public as the keyword for non-private but unique symbols, so that's ambiguous. ReallyPublic? :-P We want to capture the singleton sharing, and 'intern' is the jargon word to use. For the jargon-disabled, I'm not sure what to use, but perhaps teaching people about intern'ing is better than using some long Java-esque name.
On Oct 4, 2012, at 11:29 AM, Brendan Eich wrote:
Tab Atkins Jr. wrote:
It might be useful to expose this functionality with a more obvious name, to underscore that you lose the secrecy/unforgability. Symbol.public()?
We are mooting public as the keyword for non-private but unique symbols, so that's ambiguous. ReallyPublic? :-P We want to capture the singleton sharing, and 'intern' is the jargon word to use. For the jargon-disabled, I'm not sure what to use, but perhaps teaching people about intern'ing is better than using some long Java-esque name.
lookup? interned?
Note that in most cases, you want to look up an already interned symbol name rather than intern a new one. If the lookup falls back to creating, typos will tend to get hidden.
BTW, other than as a place to hang this function, I still don't see why we need a named Symbol constructor.
symbol @foo;
and
symbol @foo = new Symbol.
mean exactly the same thing, assuming Symbol has its default binding.
I just don't see what value we get from cluttering the name spaces with a built-in constructor that doesn't add any new functionality.
On Thu, Oct 4, 2012 at 2:29 PM, Brendan Eich <brendan at mozilla.com> wrote:
Tab Atkins Jr. wrote:
It might be useful to expose this functionality with a more obvious name, to underscore that you lose the secrecy/unforgability. Symbol.public()?
We are mooting public as the keyword for non-private but unique symbols, so that's ambiguous. ReallyPublic? :-P We want to capture the singleton sharing, and 'intern' is the jargon word to use. For the jargon-disabled, I'm not sure what to use, but perhaps teaching people about intern'ing is better than using some long Java-esque name.
Maybe Symbol.namespace('iterator') to communicate that you're referencing the global System namespace pool. Anyone can easily roll their own namespace pools too.
Allen Wirfs-Brock wrote:
On Oct 4, 2012, at 11:29 AM, Brendan Eich wrote:
Tab Atkins Jr. wrote:
It might be useful to expose this functionality with a more obvious name, to underscore that you lose the secrecy/unforgability. Symbol.public()? We are mooting public as the keyword for non-private but unique symbols, so that's ambiguous. ReallyPublic? :-P We want to capture the singleton sharing, and 'intern' is the jargon word to use. For the jargon-disabled, I'm not sure what to use, but perhaps teaching people about intern'ing is better than using some long Java-esque name.
lookup?
Could have, but see below.
interned?
Need a verb, Senator. (Obscure Doonesbury ref, sorry.)
Note that in most cases, you want to look up an already interned symbol name rather than intern a new one. If the lookup falls back to creating, typos will tend to get hidden.
I hope not! Otherwise the typo is going to propagate anyway, especially with reverse FQDNs or such awfulness:
public @iterator = Symbol.lookup('org.emca-international.org.es6.iterator'); if (!@iterator) { @iterator = Symbol.intern('org.emca-international.org.es6.iterator'); // oops... } // d'oh!
Of course, this treats the @iterator name as a free variable that can be falsy-tested. But it's way, way to much to expect everyone to write and avoid typo plus copy/paste propagation of same!
BTW, other than as a place to hang this function, I still don't see why we need a named Symbol constructor.
symbol @foo;
and
symbol @foo = new Symbol.
Aside: we did not agree on a contextual 'symbol' keyword, and public is still in the running.
We did talk about a Symbol constructor at the meeting two weeks ago, in order to support diagnost/debugging string association. This was in the private name objects proposal going way back.
mean exactly the same thing, assuming Symbol has its default binding.
I just don't see what value we get from cluttering the name spaces with a built-in constructor that doesn't add any new functionality.
I'd agree if there wasn't "new" functionality, but there is and we've discussed it. It's in the wiki.
Dean Landolt wrote:
On Thu, Oct 4, 2012 at 2:29 PM, Brendan Eich <brendan at mozilla.com <mailto:brendan at mozilla.com>> wrote:
Tab Atkins Jr. wrote: It might be useful to expose this functionality with a more obvious name, to underscore that you lose the secrecy/unforgability. Symbol.public()? We are mooting public as the keyword for non-private but unique symbols, so that's ambiguous. ReallyPublic? :-P We want to capture the singleton sharing, and 'intern' is the jargon word to use. For the jargon-disabled, I'm not sure what to use, but perhaps teaching people about intern'ing is better than using some long Java-esque name.
Maybe Symbol.namespace('iterator') to communicate that you're referencing the global System namespace pool. Anyone can easily roll their own namespace pools too.
Urgh, namespace is misleading, it suggests Common Lisp's symbol packages, the AS3/ES4 namespaces, XML namespaces. Here, @iterator is not a prefix or part of a pair, or set-of-symbols. It's just a symbol you can find from a string.
On Oct 4, 2012, at 11:50 AM, Brendan Eich wrote:
Allen Wirfs-Brock wrote:
...
Note that in most cases, you want to look up an already interned symbol name rather than intern a new one. If the lookup falls back to creating, typos will tend to get hidden.
I hope not! Otherwise the typo is going to propagate anyway, especially with reverse FQDNs or such awfulness:
public @iterator = Symbol.lookup('org.emca-international.org.es6.iterator'); if (!@iterator) { @iterator = Symbol.intern('org.emca-international.org.es6.iterator'); // oops... } // d'oh!
Of course, this treats the @iterator name as a free variable that can be falsy-tested. But it's way, way to much to expect everyone to write and avoid typo plus copy/paste propagation of same!
The strawman for at-name declarations with initializers do not allow binding of an at-name to anything other than a symbol value. The above would throw if Symbol.lookup returned undefined or anything else that was not a symbol value.
BTW, other than as a place to hang this function, I still don't see why we need a named Symbol constructor.
symbol @foo;
and
symbol @foo = new Symbol.
Aside: we did not agree on a contextual 'symbol' keyword, and public is still in the running.
Right we didn't agree on public vs symbol vs unique. I just used "symbol" to avoid the appearance that there was consensus around "public".
We did talk about a Symbol constructor at the meeting two weeks ago, in order to support diagnost/debugging string association. This was in the private name objects proposal going way back.
mean exactly the same thing, assuming Symbol has its default binding.
I just don't see what value we get from cluttering the name spaces with a built-in constructor that doesn't add any new functionality.
I'd agree if there wasn't "new" functionality, but there is and we've discussed it. It's in the wiki.
Yes, I originally proposed the idea of associating a debug name with a symbol value. However, I don't remember it specifically being mentioned at the meeting as a justification for the constructor.
I'm not sure if that debug usage, by itself, would be enough to convince me, that we need the Symbol constructor. It is more compelling if there is more functionality such as lookup/intern that needs to have a home.
On Thu, Oct 4, 2012 at 2:51 PM, Brendan Eich <brendan at mozilla.com> wrote:
Dean Landolt wrote:
On Thu, Oct 4, 2012 at 2:29 PM, Brendan Eich <brendan at mozilla.com<mailto:
brendan at mozilla.com>> wrote:
Tab Atkins Jr. wrote: It might be useful to expose this functionality with a more obvious name, to underscore that you lose the secrecy/unforgability. Symbol.public()? We are mooting public as the keyword for non-private but unique symbols, so that's ambiguous. ReallyPublic? :-P We want to capture the singleton sharing, and 'intern' is the jargon word to use. For the jargon-disabled, I'm not sure what to use, but perhaps teaching people about intern'ing is better than using some long Java-esque name.
Maybe Symbol.namespace('iterator') to communicate that you're referencing the global System namespace pool. Anyone can easily roll their own namespace pools too.
Urgh, namespace is misleading, it suggests Common Lisp's symbol packages, the AS3/ES4 namespaces, XML namespaces. Here, @iterator is not a prefix or part of a pair, or set-of-symbols. It's just a symbol you can find from a string.
I admit the historical baggage may be too much to shake but what we're talking about here is quite precisely a namespace :)
Brendan wrote:
public @iterator = Symbol.intern('iterator');
Allen wrote:
public @iterator = Symbol('iterator');
[...]
public @iterator = RegistryOfWellKnownSymbols.lookup('iterator');
The big advantage of the last one is that RegistryOfWellKnownSymbols could be virtualized, so that code operating within such an environment looks up a different symbol under the name 'iterator'.
Dean Landolt wrote:
On Thu, Oct 4, 2012 at 2:51 PM, Brendan Eich <brendan at mozilla.com <mailto:brendan at mozilla.com>> wrote:
Urgh, namespace is misleading, it suggests Common Lisp's symbol packages, the AS3/ES4 namespaces, XML namespaces. Here, @iterator is not a prefix or part of a pair, or set-of-symbols. It's just a symbol you can find from a string.
I admit the historical baggage may be too much to shake but what we're talking about here is quite precisely a namespace :)
I don't think so, but definitions vary. Could you cite a source for yours?
On Thu, Oct 4, 2012 at 11:41 AM, Allen Wirfs-Brock <allen at wirfs-brock.com>wrote:
Note that in most cases, you want to look up an already interned symbol name rather than intern a new one.
One of the beautiful things about interning is that it is a pure function, and so does not provide a communications channel. If one frame could test whether some other frame somewhere had already interned a given name, you'd have an unpluggable cross-frame communications channel.
Presumably, this concern would also apply to associating programmer supplied debug info with symbols
It depends what you mean by "associating". What do you have in mind?
The private name proposal included the possibility of attaching a string value to a symbol when it is created. The string could be used in debug output (toString) for the symbol.
Not a mutable store. The interned symbols table is.
Can we get back to the somewhat pressing problem of @iterator vs. 'iterator' vs. Symbol.intern('iterator')? Firefox implements 'iterator' currently, happy to change, no need to rush, but the trade-offs are pretty clear.
Kevin Smith pointed out the problem for libraries trying to work cross-frame and cross-version. Tab was +1 on Symbol.intern. I am too, since the conflict between unique symbols and cross-frame singleston symbols is irreducible. The alternative of a singleton module shared among several realms is unproposed, unclear, and overkill for the @iterator-cross-frame problem.
Anyone have a better idea?
For a fresh symbol and an immutable association to a string fixed at creation time, no problem.
For an interned symbol whose associated string name is the string interned to get it, no problem.
On Thu, Oct 4, 2012 at 3:24 PM, Brendan Eich <brendan at mozilla.com> wrote:
Dean Landolt wrote:
On Thu, Oct 4, 2012 at 2:51 PM, Brendan Eich <brendan at mozilla.com<mailto:
brendan at mozilla.com>> wrote:
Urgh, namespace is misleading, it suggests Common Lisp's symbol packages, the AS3/ES4 namespaces, XML namespaces. Here, @iterator is not a prefix or part of a pair, or set-of-symbols. It's just a symbol you can find from a string.
I admit the historical baggage may be too much to shake but what we're talking about here is quite precisely a namespace :)
I don't think so, but definitions vary. Could you cite a source for yours?
I just meant in the literal sense: a "namespace" is just a string key space. Using symbols as interned strings allows you to add any number of "namespaces" to an object, including some language-defined "system" namespace. And this is what we're after, isn't it?
Dave had a suggestion for an alternative name to Symbol.intern, it's so obvious in hindsight:
public @iterator = Symbol.from('iterator');
No jargon, perhaps at the price of connoting singleton symbol, but still.
On Oct 4, 2012, at 4:02 PM, Brendan Eich wrote:
Dave had a suggestion for an alternative name to Symbol.intern, it's so obvious in hindsight:
public @iterator = Symbol.from('iterator');
No jargon, perhaps at the price of connoting singleton symbol, but still.
Symbol.for('iterator')
actually sounds to m a bit closer to the right concept.
Allen Wirfs-Brock wrote:
On Oct 4, 2012, at 4:02 PM, Brendan Eich wrote:
Dave had a suggestion for an alternative name to Symbol.intern, it's so obvious in hindsight:
public @iterator = Symbol.from('iterator');
No jargon, perhaps at the price of connoting singleton symbol, but still.
Symbol.for('iterator')
actually sounds to m a bit closer to the right concept.
Nice, I buy it. Connotation of intern'ing or memoization, compared to "from" which suggests conversion.
Kevin should weigh in, since he is thread-starter.
Kevin should weigh in, since he is thread-starter.
I'm blushing...
I'd like to back up a bit. Just to be clear, any two occurrences of:
Symbol.from("abcdefg"); // or Symbol.for...
from any module, in any global context, will result in the same symbol?
On Thu, Oct 4, 2012 at 8:36 PM, Kevin Smith <khs4473 at gmail.com> wrote:
I'd like to back up a bit. Just to be clear, any two occurrences of:
Symbol.from("abcdefg"); // or Symbol.for...
from any module, in any global context, will result in the same symbol?
Yes, that's the intent.
Yes, that's the intent.
And the strings supplied to Symbol.for/from form a single namespace across the entire executing environment?
So if an author wanted to guarantee no conflicts, they would need to use a suitable globally unique string (like a uuid or reverse DNS name, or something else of their choice)?
Kevin Smith wrote:
Yes, that's the intent.
And the strings supplied to Symbol.for/from form a single namespace across the entire executing environment?
Across the observably connected realms. Independent window/frame graphs that are disconnected, no need or way to tell.
So if an author wanted to guarantee no conflicts, they would need to use a suitable globally unique string (like a uuid or reverse DNS name, or something else of their choice)?
Something like that, in general.
A particular set of related scripts might make simplifying assumptions.
And the strings supplied to Symbol.for/from form a single namespace across
the entire executing environment?
Across the observably connected realms. Independent window/frame graphs that are disconnected, no need or way to tell.
Ah yes.
Sounds good. As an aside, does the symbol in this case provide any function other than "wrapping" the string itself? Does the symbol carry any information that the string does not, from the point of view of the script?
On Fri, Oct 5, 2012 at 8:21 AM, Kevin Smith <khs4473 at gmail.com> wrote:
Sounds good. As an aside, does the symbol in this case provide any function other than "wrapping" the string itself? Does the symbol carry any information that the string does not, from the point of view of the script?
No, in this case the results of Symbol.for
are just a duplicate of
the space of strings (just the way interned symbols are in Lisp).
On 5 October 2012 14:26, Sam Tobin-Hochstadt <samth at ccs.neu.edu> wrote:
On Fri, Oct 5, 2012 at 8:21 AM, Kevin Smith <khs4473 at gmail.com> wrote:
Sounds good. As an aside, does the symbol in this case provide any function other than "wrapping" the string itself? Does the symbol carry any information that the string does not, from the point of view of the script?
No, in this case the results of
Symbol.for
are just a duplicate of the space of strings (just the way interned symbols are in Lisp).
Indeed, which is why I'm not sure I understand what this idea is trying to achieve. Is it more than just an ad hoc way to introduce a second namespace?
-----Original Message----- From: es-discuss-bounces at mozilla.org [mailto:es-discuss-bounces at mozilla.org] On Behalf Of Andreas Rossberg Sent: Friday, October 5, 2012 14:46
On 5 October 2012 14:26, Sam Tobin-Hochstadt <samth at ccs.neu.edu> wrote:
On Fri, Oct 5, 2012 at 8:21 AM, Kevin Smith <khs4473 at gmail.com> wrote:
Sounds good. As an aside, does the symbol in this case provide any function other than "wrapping" the string itself? Does the symbol carry any information that the string does not, from the point of view of the script?
No, in this case the results of
Symbol.for
are just a duplicate of the space of strings (just the way interned symbols are in Lisp).
Indeed, which is why I'm not sure I understand what this idea is trying to achieve. Is it more than just an ad hoc way to introduce a second namespace?
Yes, is this noticeably better than just saying "use '_space_of_strings<string>'"? What does this new API accomplish that can't already be done with a conventional prefix in the normal space of strings?
2012/10/5 Domenic Denicola <domenic at domenicdenicola.com>
Indeed, which is why I'm not sure I understand what this idea is trying to achieve. Is it more than just an ad hoc way to introduce a second namespace?
Yes, is this noticeably better than just saying "use '_space_of_strings<string>'"? What does this new API accomplish that can't already be done with a conventional prefix in the normal space of strings?
I also find Symbol.for questionable. If everyone starts to define their symbols using Symbol.for, we have achieved nothing in the way of uniqueness/unforgeability.
It seems to me the only reasonable way to deal with globally unique symbols is to always make sure that any object containing symbols that crosses frames is serialized and unserialized in such a way that a "global" symbol from the originating frame is deserialized into the corresponding "global" symbol from the recipient frame. That's how one needs to deal with these issues in distributed computing between isolated processes as well.
If symbols break across frames, I think the fault lies not with the symbols. I think the fault lies with the fact that the object on which the symbol was defined didn't "properly" cross the frame boundaries. Perhaps we need better abstractions to interpose between frame boundaries?
On Fri, Oct 5, 2012 at 8:45 AM, Andreas Rossberg <rossberg at google.com>wrote:
On 5 October 2012 14:26, Sam Tobin-Hochstadt <samth at ccs.neu.edu> wrote:
On Fri, Oct 5, 2012 at 8:21 AM, Kevin Smith <khs4473 at gmail.com> wrote:
Sounds good. As an aside, does the symbol in this case provide any function
other than "wrapping" the string itself? Does the symbol carry any information that the string does not, from the point of view of the script?
No, in this case the results of
Symbol.for
are just a duplicate of the space of strings (just the way interned symbols are in Lisp).Indeed, which is why I'm not sure I understand what this idea is trying to achieve. Is it more than just an ad hoc way to introduce a second namespace?
Symbols already introduce a second namespace, and in a way that allows us represent infinitely many ad hoc string namespaces side-by-side, with no need for nesting (which means the cardinality of symbols > strings I
guess). But Symbol.for wouldn't be an ad hoc namespace -- IIUC it would be * the* de jure namespace to map string names to references for system-wide concepts like the iterator symbol.
IMHO the module namespace is a very similar to the module namespace, perhaps too similar to justify Symbol.for. Wouldn't it be easier to spec. some subset of the module namespace to behave in the manner described for Symbol.for? In fact this is exactly how I imagined the @ module prefix (often used by Brendan in module examples) would work.
On Fri, Oct 5, 2012 at 9:51 AM, Dean Landolt <dean at deanlandolt.com> wrote:
On Fri, Oct 5, 2012 at 8:45 AM, Andreas Rossberg <rossberg at google.com>wrote:
On 5 October 2012 14:26, Sam Tobin-Hochstadt <samth at ccs.neu.edu> wrote:
On Fri, Oct 5, 2012 at 8:21 AM, Kevin Smith <khs4473 at gmail.com> wrote:
Sounds good. As an aside, does the symbol in this case provide any function
other than "wrapping" the string itself? Does the symbol carry any information that the string does not, from the point of view of the script?
No, in this case the results of
Symbol.for
are just a duplicate of the space of strings (just the way interned symbols are in Lisp).Indeed, which is why I'm not sure I understand what this idea is trying to achieve. Is it more than just an ad hoc way to introduce a second namespace?
I really need learn to proofread better! Corrected below...
Symbols already introduce a second namespace, and in a way that allows us
Indeed, which is why I'm not sure I understand what this idea is trying to achieve. Is it more than just an ad hoc way to introduce a second namespace?
Presumably, so that such a namespace can be used with a property-name-substitution syntax.
A question: is
var symbol = new Symbol.for("abcdefg");
really a symbol? Or is it just a string, masquerading as a symbol?
If we answer that it's just a string, then perhaps instead of extending the concept of Symbol to include "wrapped" strings, we should extend the concept of property-name-substitution to include string targets.
On Fri, Oct 5, 2012 at 8:45 AM, Andreas Rossberg <rossberg at google.com> wrote:
Indeed, which is why I'm not sure I understand what this idea is trying to achieve. Is it more than just an ad hoc way to introduce a second namespace?
I think what's going on as follows:
- Symbols, even when not used for encapsulated abstractions, are great for avoiding the possibility of collision in the global string namespace
- So, we (tc39) decided to use them for to replace the property name currently called "iterator" in Spidermonkey.
- Currently, "iterator" works across same-origin frames, but a naive use of Symbol for this wouldn't work
- Therefore, we have a few options: 1 Give up on Symbols for "iterator" 2 Make the Symbol replacement for "iterator" magically work across all same-origin frames 3 Make iteration not work across frames 4 Break the web and fix cross-frames to work more sensibly
- Since the latter two of those are not actual options, (2) seemed like the best choice.
- But then we, the language, are doing something that programmers can't do, so we searched for something else
- This led to
Symbol.for
, which is not actually allowing programmers to do (2), but resembles it somewhat
I think we should rethink this whole direction. The bizarreness of
cross-frame interaction is real, and we have to deal with it. That
means abstractions based on libraries that provide values with
identity won't work cross-frame. I don't think Symbol.for
makes
solving any problems that we currently have easier. Symbols are great
when they're based on sharing values in the heap, and otherwise, we're
stuck with strings. We can make @iterator a magic Symbol, or we can
stick with a string, and I don't have a good sense of what the right
choice is there, but I think that's separable from Symbol.for
.
Note also that Symbol.for
has some really weird behavior. For
example, what does this evaluate to?
Symbol.for("x") instanceof Symbol
That depends if someone has previously evaluated Symbol.for("x")
in
a different frame.
- Symbols, even when not used for encapsulated abstractions, are great for avoiding the possibility of collision in the global string namespace
- So, we (tc39) decided to use them for to replace the property name currently called "iterator" in Spidermonkey.
- Currently, "iterator" works across same-origin frames, but a naive use of Symbol for this wouldn't work
- Therefore, we have a few options: 1 Give up on Symbols for "iterator" 2 Make the Symbol replacement for "iterator" magically work across all same-origin frames 3 Make iteration not work across frames 4 Break the web and fix cross-frames to work more sensibly
- Since the latter two of those are not actual options, (2) seemed like the best choice.
- But then we, the language, are doing something that programmers can't do, so we searched for something else
- This led to
Symbol.for
, which is not actually allowing programmers to do (2), but resembles it somewhat
Good breakdown, but note that the problem isn't strictly confined to cross-frame situations. See last paragraph of the original post about protocol versioning.
On Fri, Oct 5, 2012 at 7:11 AM, Sam Tobin-Hochstadt <samth at ccs.neu.edu>wrote:
Note also that
Symbol.for
has some really weird behavior. For example, what does this evaluate to?Symbol.for("x") instanceof Symbol
That depends if someone has previously evaluated
Symbol.for("x")
in a different frame.
If this is true, then it is obviously a fatal problem with the whole proposal, independent of all other issues. Do symbols have a [[Prototype]] whose value is a Symbol.prototype?
On Fri, Oct 5, 2012 at 11:23 AM, Mark S. Miller <erights at google.com> wrote:
On Fri, Oct 5, 2012 at 7:11 AM, Sam Tobin-Hochstadt <samth at ccs.neu.edu> wrote:
Note also that
Symbol.for
has some really weird behavior. For example, what does this evaluate to?Symbol.for("x") instanceof Symbol
That depends if someone has previously evaluated
Symbol.for("x")
in a different frame.If this is true, then it is obviously a fatal problem with the whole proposal, independent of all other issues. Do symbols have a [[Prototype]] whose value is a Symbol.prototype?
There are actually a couple other options here:
- Symbol values have no prototype (thus the expression above is always false)
- The
Symbol
prototype is shared across all frames (but what is its prototype?)
On Fri, Oct 5, 2012 at 7:11 AM, Sam Tobin-Hochstadt <samth at ccs.neu.edu> wrote:
Note also that
Symbol.for
has some really weird behavior. For example, what does this evaluate to?Symbol.for("x") instanceof Symbol
That depends if someone has previously evaluated
Symbol.for("x")
in a different frame.
This isn't weird at all. It's "true" regardless of whether Symbol.for("x") has been evaluated elsewhere.
Symbol.for("x") creates a Symbol with forgeability (so that other frames can successfully forge the Symbol and buy into your API). It doesn't need to create identical objects, any more than String("x") needs to create identical objects every time. So long as every instance of Symbol.for("x") can be interchanged as a property name, you're fine.
(In your assumed behavior, Symbol.for() would still be an unpluggable cross-frame messaging hole, contrary to Mark Miller's goals. It would be low bandwidth, of course (one bit per Symbol), but still.)
On Oct 5, 2012, at 7:11 AM, Sam Tobin-Hochstadt wrote:
On Fri, Oct 5, 2012 at 8:45 AM, Andreas Rossberg <rossberg at google.com> wrote:
Indeed, which is why I'm not sure I understand what this idea is trying to achieve. Is it more than just an ad hoc way to introduce a second namespace?
I think what's going on as follows:
- Symbols, even when not used for encapsulated abstractions, are great for avoiding the possibility of collision in the global string namespace
- So, we (tc39) decided to use them for to replace the property name currently called "iterator" in Spidermonkey.
- Currently, "iterator" works across same-origin frames, but a naive use of Symbol for this wouldn't work
(note @@foo is how, within the specification, I refer to the value of a well known symbol)
This isn't just about @@iterator. @@iterator is just one example of a place where an object property is use to complete or extend the semantics of a language construct or built-in library feature. @@iterator is a hook by which the for-of statement obtains an iterator object. @@toStringTag is a hook that is used to extend the built-in Object.prototype.toString method. The Object Model Reformation strawman proposes adding @@elementGet, @@elementSet, and @@elementDelete hooks. There have been informal proposals for a @@construct hook for the new operator. All of these usages require universal identify across all Realms (frames) that are able to interchange executable functions.
Properties with these keys all implement interfaces that face towards the language implementation. The are "meta" in at least one sense of that term. They also all exist on objects that expose other interfaces that are intended for general use. While it isn't absolutely necessary, it seems desirable to separate the keys of these meta interfaces into a distinct namespace. This prevents accidental punning uses of these meta hooks. It also permits future extension of the set of meta interface properties without conflicting with property name choices in pre-existing ES code.
Use of Symbols rather than string keys for these meta properties means that code that wishes to define (or reference) such properties need some way to first obtain access to the corresponding symbol value. That might be done via:
A built-in module:
import @iterator from "@itr"; or import @iterator from "@System";
A built-in property:
symbol @toStringTag = Object.prototype.toString.tagSymbol;
or some sort of well known registry that provides access to these symbol values via some sort of published name associations:
symbol @iterator = MetaSymbolRegistry.lookup("iterator");
or a user extensible cross realm symbol registry:
symbol @iterator = Symbol.for("iterator");
We have to make a design decision regarding which of these approaches to take, but some such approach is required if we intend to use symbols for meta property keys and and at-names to access them. Whichever form we choose, they all need to produce common symbol values that span all connected realms.
- Therefore, we have a few options: 1 Give up on Symbols for "iterator" 2 Make the Symbol replacement for "iterator" magically work across all same-origin frames 3 Make iteration not work across frames 4 Break the web and fix cross-frames to work more sensibly
- Since the latter two of those are not actual options, (2) seemed like the best choice.
- But then we, the language, are doing something that programmers can't do, so we searched for something else
- This led to
Symbol.for
, which is not actually allowing programmers to do (2), but resembles it somewhat
yes, but of course, ,it's not just @@iterator.
I think we should rethink this whole direction. The bizarreness of cross-frame interaction is real, and we have to deal with it. That means abstractions based on libraries that provide values with identity won't work cross-frame. I don't think
Symbol.for
makes solving any problems that we currently have easier. Symbols are great when they're based on sharing values in the heap, and otherwise, we're stuck with strings. We can make @iterator a magic Symbol, or we can stick with a string, and I don't have a good sense of what the right choice is there, but I think that's separable fromSymbol.for
.
To me, this is clearly a module loader level issue as it concerns the creation and initial sharing between related realms (module loader contexts).
Consider all of the above alternative techniques for accessing a well known meta symbol. They all involve either access to a a "build-iin" module that would need to be resolved by a module loader or start with a reference to a global binding that would have been initially established by a module loader. In either case, the creator of the module loader for such a realm could initialize it in a way that passes the appropriate symbol values. Anybody creating their own new symbol "namespace" that they wanted to share between realms could set up there own registry mechanism and coordinate its sharing among the realms it it creates.
Note also that
Symbol.for
has some really weird behavior. For example, what does this evaluate to?Symbol.for("x") instanceof Symbol
I haven't had time to devote to the typeof Symbol thread, so I don't want to naively jump into the middle of that discussion.
However, the above is one of the reasons that my starting point is that symbol values should be primitive values rather than object instances. Or alternatively, why Symbol should not be a constructor and instead a symbol/private declaration should be the only way to instantiate a new symbol.
However, even if they are objects and Symbol is a constructor, instanceof Symbol can be made to work across realms simply by specifying the [[HasInstance]] internal method of Symbol appropriately.
That depends if someone has previously evaluated
Symbol.for("x")
in a different frame.
No, the intended semantics of Symbol.for is that it maintains a single cross realm (frame) registry of symbol values.
Le 05/10/2012 17:23, Mark S. Miller a écrit :
On Fri, Oct 5, 2012 at 7:11 AM, Sam Tobin-Hochstadt <samth at ccs.neu.edu <mailto:samth at ccs.neu.edu>> wrote:
Note also that `Symbol.for` has some really weird behavior. For example, what does this evaluate to? Symbol.for("x") instanceof Symbol That depends if someone has previously evaluated `Symbol.for("x")` in a different frame.
If this is true, then it is obviously a fatal problem with the whole proposal, independent of all other issues. Do symbols have a [[Prototype]] whose value is a Symbol.prototype?
Or more "profoundly", are symbols Objects (in the ES5.1 - 8.6 section sense) at all or just lightweigh representations of object identities?
Tom Van Cutsem wrote:
2012/10/5 Domenic Denicola <domenic at domenicdenicola.com <mailto:domenic at domenicdenicola.com>>
> Indeed, which is why I'm not sure I understand what this idea is trying to achieve. Is it more than just an ad hoc way to introduce a second namespace? Yes, is this noticeably better than just saying "use '__space_of_strings_<string>'"? What does this new API accomplish that can't already be done with a conventional prefix in the normal space of strings?
I also find Symbol.for questionable. If everyone starts to define their symbols using Symbol.for, we have achieved nothing in the way of uniqueness/unforgeability.
Yes, all we'll have done is dodged a name collision with a string-equated property name, at the price of new API and boilerplate using it, plus risk of collision on the argument to Symbol.for!
For the specific case of the iteration protocol, I'd rather use 'iterator' (no dunder affixes) at that point.
If symbols break across frames, I think the fault lies not with the symbols. I think the fault lies with the fact that the object on which the symbol was defined didn't "properly" cross the frame boundaries. Perhaps we need better abstractions to interpose between frame boundaries?
Good point. See
developer.mozilla.org/en-US/docs/XPConnect_security_membranes#XPCCrossOriginWrapper
where we nevertheless took advantage of same-process reachability among same-origin frames/windows.
This diagram is now out of date. We always membrane-wrap when crossing frame or window boundaries in Gecko. The membrane for same-origin window boundaries is quite thin, but it could handle symbol unification as you suggest.
I think there is an idea which hasn't been mentionned yet which would be to let programmers "merge" or "assimilate" cross-frame symbols. Basically, when receiving a symbol from some frame, it'd be possible to say "I assimilate this symbol I received from another frame to my own persistableSymble". I haven't thought about all the cases and details, but the idea behind it is to let users match symbols the way they wish preserving unforgeability.
I think it would provide a way to solve both cross-frame and maybe dependency-tree issues.
David
Le 03/10/2012 19:40, Kevin Smith a écrit :
This approach is kind of what I was getting at with my postMessage point:
window.onmessage = function (data) { let iterator2 = data.iterator; };
But it was pointed out this was rather un-usable, leading to the current discussion.
I still would like to see someone respond to jjb's message though: esdiscuss/2012-October/025531
From: es-discuss-bounces at mozilla.org [mailto:es-discuss-bounces at mozilla.org] On Behalf Of David Bruant Sent: Saturday, October 6, 2012 13:47 To: Kevin Smith Cc: es-discuss Subject: Re: Symbols, Protocols, Frames, and Versioning
I think there is an idea which hasn't been mentionned yet which would be to let programmers "merge" or "assimilate" cross-frame symbols. Basically, when receiving a symbol from some frame, it'd be possible to say "I assimilate this symbol I received from another frame to my own persistableSymble". I haven't thought about all the cases and details, but the idea behind it is to let users match symbols the way they wish preserving unforgeability.
I think it would provide a way to solve both cross-frame and maybe dependency-tree issues.
David
Le 03/10/2012 19:40, Kevin Smith a écrit :
One of the main use cases for symbols is for defining object "protocols" that don't suffer from property name conflicts. The recently discussed iterator
and toStringTag
method names fall into this category. The idea is that we can implement the protocol by defining methods using symbols, and thus avoid namespacing considerations.
Designing and maintaining a global namespace is, well, no fun.
But consider the multiple-global case in which we have scripts running in more than one frame. It seems like protocols should be transferrable across frames. For built-in protocols like iterator
, this has to work:
function f(iterable) {
for (x of iterable) {
// This must work regardless of which frame `iterable` comes from
}
}
But what about user-defined protocols? Let's say we have a "Persistable" protocol:
export var persistName = new Symbol; // unique, not "private"
And a function which makes use of this protocol:
import persistName from "Persistable.js";
function usePersistable(obj) {
if (obj[persistName])
obj[persistName]();
}
It seems like usePersistable
should be able to work as expected even if obj
comes from a different frame (in which "Persistable.js" was separately loaded).
Another expression of the same problem occurs with versioning.
Suppose that in a fairly complex module dependency graph, "Persistable-0.1.js" and "Persistable-0.2.js" are simultaneously loaded. ("Persistable" is on github and therefore in perpetual version-zero purgatory.) It seems reasonable to expect that objects implementing the protocol defined by "Persistable-0.2.js" should be able to work with functions consuming the "Persistable-0.1.js" protocol. But that is not possible with unique symbols.
In summary, I don't think that we can really avoid global namespacing issues using system-generated unique symbols as we currently conceive of them. Built-in protocols like iterator
are a special "cheating" case, but we need to have an equally consistent story for user-defined protocols.
Kevin
This body part will be downloaded on demand.
Domenic Denicola wrote:
I still would like to see someone respond to jjb’s message though: esdiscuss/2012-October/025531
JJB is right about same-origin allowing sharing so making postMessage unnecessary, but same-origin does not mean a symbol created by a module loaded in one frame is the same as the symbol created by that module loaded in a related same-origin frame. That's the trouble.
On Oct 6, 2012, at 4:47 AM, David Bruant wrote:
I think there is an idea which hasn't been mentionned yet which would be to let programmers "merge" or "assimilate" cross-frame symbols. Basically, when receiving a symbol from some frame, it'd be possible to say "I assimilate this symbol I received from another frame to my own persistableSymble". I haven't thought about all the cases and details, but the idea behind it is to let users match symbols the way they wish preserving unforgeability.
I think it would provide a way to solve both cross-frame and maybe dependency-tree issues.
David
I submit that this isn't a cross-frame symbol sharing issue, it is a cross frame object sharing issue that also touches upon issues of cross frame module instance sharing.
Here is an isomorphic problem that doesn't involve symbols:
Assume you wish each frame to have a global binding named SharedObjectDispensor whose value is an object that has a single method "lookup" that takes a string argument. When presented with a string key that it it recognizes, it returns the the associated object. All frames return the same object for any given key.
Given that definition. How do you make SharedObjectDispensor visible as a global in each frame and how do you make each such global binding have the same shared dispenser object as its value.
Now a variation on the same problem:
SharedObjectDispensor is a a binding that is exported from a module externally named SharedObjects.js. Other than that it behaves the same as described above.
So, how do you implement SharedObject.js to accomplish that.
Either of these use cases seem like something that should be easily accomplished, if you are defining the module loaders that set up the frames and control module loading into the frames.
However, if you simply use HTML iframes then you are presumably using an implementation provided module loader that implements the cross-frame sharing rules defined by the html spec. Can the first approach be accomplished today using iframes?
Bottom lines, symbol sharing is such a specific case of a more general use case. Solve the general case and you should also have a solution for symbols.
On Oct 6, 2012, at 9:37 AM, Brendan Eich wrote:
Domenic Denicola wrote:
I still would like to see someone respond to jjb’s message though: esdiscuss/2012-October/025531
JJB is right about same-origin allowing sharing so making postMessage unnecessary, but same-origin does not mean a symbol created by a module loaded in one frame is the same as the symbol created by that module loaded in a related same-origin frame. That's the trouble.
Except that symbol creation isn't the issue. By definition, each time a symbol is "created" you are getting a new unique symbol. Frames/contexts/realms have nothing to to with that.
The issue, is providing access to an already created symbol across such boundaries. As I have pointed out in other messages, this isn't a problem that it unique to symbols. It is isomorphic with use cases for sharing any kind of object across such boundaries.
Le 06/10/2012 19:52, Allen Wirfs-Brock a écrit :
(...)
Either of these use cases seem like something that should be easily accomplished, if you are defining the module loaders that set up the frames and control module loading into the frames.
True. That may be a good idea to think about it from this angle, I totally agree.
However, if you simply use HTML iframes then you are presumably using an implementation provided module loader that implements the cross-frame sharing rules defined by the html spec. Can the first approach be accomplished today using iframes?
I guess not. There were some flaws in how HTML handles scripts, namely (among others) unconditional execution of inline scripts and unconditional execution of scripts whichever there source is. Both could lead to XSS as we know. Since the flaws couldn't be fixed at the content level, they've been fixed at a lower-level (CSP). It might be where to find the solution.
Assuming every set of frame is a tree, maybe an HTML attribute could define a script source which defines a module loader for how modules are being imported in the iframe?
Allen Wirfs-Brock wrote:
On Oct 6, 2012, at 9:37 AM, Brendan Eich wrote:
Domenic Denicola wrote:
I still would like to see someone respond to jjb’s message though: esdiscuss/2012-October/025531 JJB is right about same-origin allowing sharing so making postMessage unnecessary, but same-origin does not mean a symbol created by a module loaded in one frame is the same as the symbol created by that module loaded in a related same-origin frame. That's the trouble.
Except that symbol creation isn't the issue. By definition, each time a symbol is "created" you are getting a new unique symbol. Frames/contexts/realms have nothing to to with that.
I agree, but symbols are the nominal issue in this thread.
The issue, is providing access to an already created symbol across such boundaries. As I have pointed out in other messages, this isn't a problem that it unique to symbols. It is isomorphic with use cases for sharing any kind of object across such boundaries.
Indeed, as Tom said just 15 messages up-thread:
''If symbols break across frames, I think the fault lies not with the symbols. I think the fault lies with the fact that the object on which the symbol was defined didn't "properly" cross the frame boundaries. Perhaps we need better abstractions to interpose between frame boundaries?''
David Bruant wrote:
Assuming every set of frame is a tree, maybe an HTML attribute could define a script source which defines a module loader for how modules are being imported in the iframe?
It's not all frames, even now. window.open can target existing windows as well as create new ones (subject to target name visibility rules), so: graph not tree.
If we want to work on HTML/JS embedding extensions, we should use public-script-coord. Best to have a more detailed proposal in hand to discuss.
Allen Wirfs-Brock wrote:
Can the first approach be accomplished today using iframes?
Yes, using a named iframe (display:none) that everyone knows how to find, you could hack this kind of SharedObjectDispensor up today. Wouldn't be as pretty as we want, but doable so long as same-origin.
The issue, is providing access to an already created symbol across such boundaries. As I have pointed out in other messages, this isn't a problem that it unique to symbols. It is isomorphic with use cases for sharing any kind of object across such boundaries.
I disagree. The only reason this issue looks like a nail is because you're gripping that symbol hammer so hard. ; ) The problem is a quack.
This is duck-typing, plain and simple. The only issue is what kind of a quack we are looking for. Do we identify the quack by using a flat identifier namespace (as we typically do), or do we go for something stronger?
There's nothing wrong with using "iterator" as the quack. But the option exists for something stronger, if we are willing to create the syntax to support it.
Let's call it strong-duck-typing. Instead of a quack being an identifier in the flat namespace of identifier names, the quack is a globally unique string. There are only two sane options for globally unique names: uuids (or random equivalents) and DNS-based paths. Let's arbitrarily choose uuids.
We can do strong duck-typing now (and I actually use this in one of my projects):
// UUID property names used for duck-typing var ON_COMPLETE = "07b06b7e-3880-42b1-ad55-e68a77514eb9", IS_REJECTION = "7d24bf0f-d8b1-4783-b594-cec32313f6bc";
// Returns true if an object is a promise function isPromise(obj) {
return obj && obj[ON_COMPLETE];
}
// Returns true if a promise is a rejection function isRejection(obj) {
return obj && obj[IS_REJECTION] === true;
}
And with supporting syntax, we could make this really fluid:
// UUID property names used for duck-typing var onComplete = "07b06b7e-3880-42b1-ad55-e68a77514eb9", isRejection = "7d24bf0f-d8b1-4783-b594-cec32313f6bc";
// Returns true if an object is a promise function isPromise(obj) {
return obj && obj. at onComplete;
}
// Returns true if a promise is a rejection function isRejection(obj) {
return obj && obj. at isRejection === true;
}
I hope this illustrates the general idea. As I said, there's nothing wrong with using "iterator" or "__iterator" but there is another option.
On Oct 6, 2012, at 2:57 PM, Kevin Smith wrote:
The issue, is providing access to an already created symbol across such boundaries. As I have pointed out in other messages, this isn't a problem that it unique to symbols. It is isomorphic with use cases for sharing any kind of object across such boundaries.
I disagree. The only reason this issue looks like a nail is because you're gripping that symbol hammer so hard. ; ) The problem is a quack.
This is duck-typing, plain and simple. The only issue is what kind of a quack we are looking for. Do we identify the quack by using a flat identifier namespace (as we typically do), or do we go for something stronger?
There's nothing wrong with using "iterator" as the quack. But the option exists for something stronger, if we are willing to create the syntax to support it.
Let's call it strong-duck-typing. Instead of a quack being an identifier in the flat namespace of identifier names, the quack is a globally unique string. There are only two sane options for globally unique names: uuids (or random equivalents) and DNS-based paths. Let's arbitrarily choose uuids.
I don't think "strong" is the right adjective for what you are trying to say. I think "immune to unintended conflicting usage of a property name" is the idea you are getting at. It a property is named using commonly used word, it has a higher probability that somebody will choose that same word to name a property that has a different intended semantics than the original usage. They appear to be polymorphicly equivalent properties but they really aren't. They won't "act like the same duck".
Being globally unique is not sufficient. ECMAScript names are globally unique, but many string values are subject to unintended semantic conflict (reassignment of meaning) exactly because they are globally available to everybody and likely to be chosen the a good property name. DNS-based paths use of UUIDs DNS-bassed paths reduces (significantly) the probably of an unintended conflicting reuse of a name. But it does not guarantee that it won't occur.
The alternative is to use a unique value that can't be unintentionally recreated. That is what a symbol is. It is an unforgeable, unguessable property name. If you want to define a property that will be recognized to have some specific meaning you must obtain the symbol to use as the property key from some dispensing authority.
One of the main use cases for symbols is for defining object "protocols" that don't suffer from property name conflicts. The recently discussed
iterator
andtoStringTag
method names fall into this category. The idea is that we can implement the protocol by defining methods using symbols, and thus avoid namespacing considerations.Designing and maintaining a global namespace is, well, no fun.
But consider the multiple-global case in which we have scripts running in more than one frame. It seems like protocols should be transferrable across frames. For built-in protocols like
iterator
, this has to work:But what about user-defined protocols? Let's say we have a "Persistable" protocol:
And a function which makes use of this protocol:
It seems like
usePersistable
should be able to work as expected even ifobj
comes from a different frame (in which "Persistable.js" was separately loaded).Another expression of the same problem occurs with versioning.
Suppose that in a fairly complex module dependency graph, "Persistable-0.1.js" and "Persistable-0.2.js" are simultaneously loaded. ("Persistable" is on github and therefore in perpetual version-zero purgatory.) It seems reasonable to expect that objects implementing the protocol defined by "Persistable-0.2.js" should be able to work with functions consuming the "Persistable-0.1.js" protocol. But that is not possible with unique symbols.
In summary, I don't think that we can really avoid global namespacing issues using system-generated unique symbols as we currently conceive of them. Built-in protocols like
iterator
are a special "cheating" case, but we need to have an equally consistent story for user-defined protocols.