What should `Symbol(sym)` do?
Intuitively, I wouldn't expect anything else but this--I think this is just a minor authoring oversight. Please file at bugs.ecmascript.org
On Feb 11, 2014, at 6:40 AM, Claude Pache wrote:
According to the current version of the ES6 spec 1,
Symbol(desc), whendescis notundefined, coerces its argument to a string and uses it as a description for a newly created symbol. In particular, ifsymis a symbol,Symbol(sym)throws a TypeError (it can't be coerced to string).Intuitively, I would expect that
Symbol(sym)just returnssym, just likeNumber(num)returnsnum, or, more generally, just likePrimitive(x)casts its argument to the corresponding primitive.What do you think?
I can't find any explicit mention of this in the meeting notes 1 where we decided to make Symbol a primitive type with a wrapper class. However, I made the appropriate changes to the spec. immediately after that meeting when the discussion was fresh in my mind and I still think that throwing on Symbol(sym) is the right thing to do:
For the other primitive/wrapper types the following conventions apply:
new Wrapper(prim) //always creates a new Wrapper instances wrapping the supplied primitive value
Wrapper(prim) //always returns a primitive value derived by coercing the argument to the appropriate primitive type. Returns prim if same type.
At 1 we made different decisions for symbols and Symbol wrappers :
new Symbol(arg) // always throws, we don't support explicit creation of Symbol wrapper objects
Symbol(arg) // always returns a new, not previously observed, primitive symbol value
We made new Symbol throw to avoid the silent mistake where somebody uses it thinking they are actually creating a new symbol value rather than a wrapper object. Instead we decide to make a direct call to Symbol the only way (other than Symbol.for) to access a new previously unobserved symbol value.
Symbol("string") is a symbol factory call, not a coercion of "string" to a symbol. The argument is not a value to be coerced to symbol but a string value that is part of the state of the new symbol value. In particular:
console.log(Symbol("x") === Symbol("x")) //false, each call to Symbol returns a new unique symbol value
If Symbol(sym) returned sym, that would break the invariant that calling Symbol always produces a new symbol value. Rather that reenforcing the fact the Symbol has its own usage patterns that are different from Number/String/Boolean it would partially blur that distinction.
ToString(symbolValue) throws because we don't want people doing string concatenation to a symbol thinking they are manipulating a string property key.
ToString(symbolWrapper) just does a normal symbolWrapper.toString() call.
It would be consistent with the use of Symbol(foo) to allow Symbol(symbolValue) and to internally perform symbolValue.toString() [note implicit wrapping via property access) . However, that would still create a new unique symbol value which probably isn't what the programmer actually intended. Another silent mistake.
When all these factors are considered, I think what is currently specified is just fine.
One consideration is that it still is possible end up with a Symbol wrapper and with the current spec there's no way to unwrap it. The argument for having a Symbol wrapper at all was to enabled prototypal inheritance, so, in theory, we want to enable people to add methods to Symbol.prototype. When non-strict methods on Symbol.prototype are called, they'll encounter a wrapped Symbol object that they can't do much with since they can never get at the underlying Symbol primitive.
On Feb 11, 2014, at 11:20 AM, Brandon Benvie wrote:
One consideration is that it still is possible end up with a Symbol wrapper and with the current spec there's no way to unwrap it.
Symbol.prototype.valueOf returns the primitive value of a Symbol wrapper (note, I just fixed it in rev 23 so it and toString also work for unwrapped symbol value)
Ah ignore me then!
Le 11 févr. 2014 à 20:04, Allen Wirfs-Brock <allen at wirfs-brock.com> a écrit :
Symbol("string") is a symbol factory call, not a coercion of "string" to a symbol. The argument is not a value to be coerced to symbol but a string value that is part of the state of the new symbol value. In particular:
console.log(Symbol("x") === Symbol("x")) //false, each call to Symbol returns a new unique symbol valueIf Symbol(sym) returned sym, that would break the invariant that calling Symbol always produces a new symbol value. Rather that reenforcing the fact the Symbol has its own usage patterns that are different from Number/String/Boolean it would partially blur that distinction.
Good point. Symbol(x) consistently produces a different value at each call, whereas for other primitives, Primitive(x) consistently produces the same value.
If we wanted absolutely to be consistent with other primitives, we could make Symbol(...) a function that casts to symbol (and, in fact, throws a TypeError in most cases), and use another function (e.g., Symbol.spawn("name")) in order to produce new symbols. But it is probably not worth to do that.
According to the current version of the ES6 spec,
Symbol(desc), whendescis notundefined, coerces its argument to a string and uses it as a description for a newly created symbol. In particular, ifsymis a symbol,Symbol(sym)throws a TypeError (it can't be coerced to string).Intuitively, I would expect that
Symbol(sym)just returnssym, just likeNumber(num)returnsnum, or, more generally, just likePrimitive(x)casts its argument to the corresponding primitive.What do you think?