5 ms·
> ES6 symbols are similar to the more traditional symbols in languages like Lisp and Ruby, but not so closely integrated into the language. This is why I'm not
by SCdF 9y ago
> ES6 symbols are similar to the more traditional symbols in languages like Lisp and Ruby, but not so closely integrated into the language.
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.
- ajanuary 9y agoIt's also not really true: they're the opposite of symbols in lisp and ruby. In lisp and ruby, `:a == :a` is true, while in JavaScript `Symbol("a") == Symbol("a")` is false.
- sim0n 9y agoFWIW, you can use `Symbol.for` to reuse symbols in JS (`Symbol.for("a") === Symbol.for("a")` is true).
- _pmf_ 9y agoThat's perfectly in line with web devs appropriating and perverting well established terms due to a mix of willful ignorance and cultivated arrogance.
- TeMPOraL 9y agoThey are similar, but with kind of inverted defaults. 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 http://clhs.lisp.se/Body/f_mk_sym.htm and http://clhs.lisp.se/Body/f_gensym.htm http://clhs.lisp.se/Body/f_gensym.htm.
- lixquid 9y agoSymbol.for("a") != Symbol.for("b") Perhaps you meant Symbol.for("a") for both?
- TeMPOraL 9y agoThat's what I meant. Thanks! I corrected the typo.
- Touche 9y agoBut in Ruby people use symbols to specify an object's properties, and they aren't used that way in JavaScript at all. In JavaScript they are used for metadata and other things you don't want to be serialized by JSON.stringify() or Object.assign(). I've always been confused by why Ruby uses symbols so much.
- nv-vn 9y agoWell, Ruby objects are basically big hash tables in a way. Makes sense to use a prehashed string to index them (if I understand how symbols work correctly).
- yomly 9y agoUsing symbols I think is more expressive than using strings. 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...
- masklinn 9y ago> In JavaScript they are used for metadata and other things you don't want to be serialized by JSON.stringify() or Object.assign(). 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.
- masklinn 9y ago> In lisp and ruby, `:a == :a` is true, while in JavaScript `Symbol("a") == Symbol("a")` is false. But `Symbol.for("a") === Symbol.for("a")`, and Symbol("a") is very similar to (make-symbol "a").
- gvx 9y agoI'm designing a programming language that has symbols that are namespaced to objects, where two symbols are equal iff the object they're linked to is the same and their string representation is the same. 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.
- acjohnson55 9y agoWhy is that helpful? In JS, the way you accomplish the same thing is to assign the single instance of the symbol to a property in your module, and that's how you access it, rather than summoning it with the Symbol function. Not sure what the machinery you're suggesting buys you, but I could be missing something!
- gvx 9y agoI don't suppose it buys you anything JS can't do, but it does allow those things to be integrated closely in the language, in a way JS can't do without majorly breaking the language.
- masklinn 9y ago> Hopefully there will still be useful cases for them. 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
- SCdF 9y agoThanks for this list, very interesting! 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.
- masklinn 9y ago> I haven't really dealt with this level of JS before, so unfortunately I'm ignorant of every one of your bullet points. 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…
- SCdF 9y agoOh sure, yeah I see how #4 would be useful. Generally I've used / seen `undefined` used for that in the past, which can be problematic because it might mean what you think it means, or it might mean something broken in your algo by mistake.