ES6 in Depth: Symbols (2015)
hacks.mozilla.org
hacks.mozilla.org
This is why I'm not massively interested in ES6 symbols tbh, though I could be missing something.
Hopefully there will still be useful cases for them.
Symbol("a") == Symbol("a") is more-less equivalent to Common Lisp's (eq (make-symbol "a") (make-symbol "a")), both of which will return false, because (in Lisp terms) we're creating two different symbols. In Common Lisp, the default behaviour of a reader if it sees a stand-alone symbol token, is to intern it - put it in "global"[0] storage, so that if the reader sees the same-looking symbol token again, it returns the same symbol instance it previously interned.
In JavaScript, per this article, the interning behaviour is available through Symbol.for(), i.e. Symbol.for("a") == Symbol.for("a") should return true, much like (eq 'a 'a) would return true in Lisp.
--
[0] - it's actually per-package storage, but that's getting into the more advanced details of CL.
--
EDIT replaced gensym with make-symbol in Common Lisp example, as the former is used primarily in writing macros, so it ensures the created symbol has an unique printed representation, which helps make macroexpansions more readable.
(make-symbol "FOO") -> #:FOO
(make-symbol "FOO") -> #:FOO ; NOT eq to the previous one
(gensym "FOO") -> #:FOO103
(gensym "FOO") -> #:FOO104 ; note the incrementing counter appended by gensym
Compare http://clhs.lisp.se/Body/f_mk_sym.htm and http://clhs.lisp.se/Body/f_gensym.htm.Perhaps you meant Symbol.for("a") for both?
I like the separation of strings for external data and symbols for internal data.
So symbols will usually be used for enums or things which logic or control flow depend on. Symbols then have the benefit of feeling like syntax and the scheme zen of code is data is code...
They're used to avoid conflicts for "generic method" hooks (similar to dunder methods in Python): the conflict issues is why environments have broken when new methods were added, and why e.g. Object.keys and their ilk are "static" functions rather than methods.
If you don't want JSON.stringify or Object.assign to see your properties just make them non-enumerable e.g.:
> const obj = {};
> Object.defineProperty(obj, 'prop', {'value': 42});
> obj
< {prop: 42}
> JSON.stringify(obj)
< "{}"
> Object.assign({}, obj)
< {}
Object.assign will copy symbol-keyed properties. In fact Symbol-keyed properties are enumerable by default.They live in an odd half-way world because many constructs only accept/handle string-keyed properties: Object.keys/entries/values and JSON.stringify use EnumerableOwnProperties and (for..in) uses EnumerateObjectProperties, both of which are specified to ignore non-string-keyed properties (before even checking for enumerability).
> I've always been confused by why Ruby uses symbols so much.
Mutable non-interned strings.
Good: Other people mainly covered it. Also note that when writing extensions using the C API, accepting options as symbols is generally much more convenient.
Less good:
val = "something"
val = :something
# Saves one character and no need for an end token.
{ "key" => val }
{ key: val }
# Saves four characters.But `Symbol.for("a") === Symbol.for("a")`, and Symbol("a") is very similar to (make-symbol "a").
In that language, the equivalent of :a == :a would be true, and you would use different "namespace objects" to prevent collisions.
For example, classes that implement the function interface could provide methods named call and partial, that are namespaced to the function class object, leaving you free to define methods with those names but different namespaces for completely different purposes.
There are already useful cases for them:
* non-conflicting extensions to existing prototypes, in fact most of the standard uses of symbol are exactly that, it finally allows the standard to add new methods to existing types (Object, String, Regex, …) without breaking everything and risking conflict with existing extensions
* similarly non-conflicting properties to non-prototype third-party objects for e.g. decorators and proxies and the like (sometimes you can't avoid bundling your data as part of the original payload)
* much like (gensym) though somewhat less convenient, can be used to avoid conflicts when doing codegen
* more convenient/debuggable sentinel object than e.g. `{}` which would tell you very little when you unwittingly leaked it, especially as symbols may be the only builtin object which is not implicitly convertible to a string
I haven't really dealt with this level of JS before, so unfortunately I'm ignorant of every one of your bullet points. Good to see it's got lots of uses though.
That's not necessarily a bad thing, #1 is mostly of interest to the standard committee and historically considered a bad move (and in fact the source of the issue) for third parties (you may have to use its result if you want to e.g. make one of your objects iterable though), #2 is something you do when all options are exhausted, #3 is when you write your own codegen.
#4 is probably the one you're most likely to encounter, it's not that common either, and `null` or `undefined` generally work as sentinels so…
In particular, hasInstance is apparently implemented by some browsers now:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...