Hacker News new | ask | show | jobs
by Gorkys 4192 days ago
Could anybody explain what problems this was designed to solve?
6 comments

Same use (albeit with less nice syntax) as the ruby symbols.

Many frameworks based on ruby (like Rails) make heavy use of symbols.

They are handy as they are immutable, very cheaply comparable for equality and (in ruby) very light on syntax and they can be made out of thin air whenever you need one.

    some_array[:foo] = 'bar'
instead of

    FOO=0
    some_array[FOO] = 'bar';
(think how error prone it's going to be to add more keys like this. Forgotten declarations, duplication declarations and so on).

You could use strings, but they are often heap-allocated (slow) and sometimes (ruby) even mutable and thus way more expensive to compare for equality and require some kind of hashing to put them as keys into hash tables. Symbols don't. And never conflict.

My idea of them was, they are like string-identifiers, but instead of creating a new string for every string-literal the symbol-literal creates one global, immutable instance.
The main reason why Symbols were added AFAIK is to specify protocols / interfaces. Right now, duck typing is all we have in JS.

How do you check if an object is a Promise? You do `(typeof obj.then === 'function')`. Is this good? No, not really, especially not when the language reserves such a word - the problem is that they're global names and can easily conflict with existing methods.

In contrast, a symbol is used for the Iterator protocol [1]. You implement the protocol by adding a method to your object at the key "Symbol.iterator" that returns an iterator object. Unlike with string keys, its impossible for someone to have already used Symbol.iterator as a key before: the value didn't exist at all. As a result, this feature lets ES.next and libraries define new protocols without the need to come up with unique method names that aren't used in any library.

[1]: https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/T...

In addition to the other replies, Symbols were at one point meant to be used for implementing private visibility in classes - the idea being that the private fields of an object would only be visible inside the class closure, preventing external code from being able to access them. However, private has been backed off from ES6 now.
To be clear, "private" variables (including "methods", ie. variables bound to functions) can be implemented anywhere using lexical scope; whether you're pretending to have "classes" or not.

The feature which symbols offer is unforgeable contracts. These offer some benefits of type systems and module systems (AKA existential type systems). Any time we want to restrict access to some value, we can "seal" it by generating a new symbol and using that as its key in an object/mapping. We can pass the symbol to the code we want to "unseal" it, whilst the object itself can get passed around anywhere at all, without anyone being able to extract or replace the value until it reaches the "unsealer".

To see why this would be useful for "private visibility", the trick is not that we're hiding the values to everyone outside our lexical scope (which is trivial, since that's what lexical scope is); it's that we can allow access to these values, via functions which check that they're given the correct symbol. In other words, we can have our code "recognise" when some other code should be given access. If we limit ourselves to making "classes", where the symbol is in-scope in the "constructor" and doesn't leak out, then we have a primitive form of type-checking; if we're given the correct symbol, the other code must be of the same "class", so we might decide to let it see our "privates".

Of course, that's just a degenerate edge-case. There are all kinds of other uses, for example section XII of Harper's Practical Foundations for Programming Languages is dedicated to the topic http://www.cs.cmu.edu/~rwh/plbook/book.pdf

>To be clear, "private" variables (including "methods", ie. variables bound to functions) can be implemented anywhere using lexical scope; whether you're pretending to have "classes" or not.

Sure, as long as you declare all methods that require access to said private fields also within that same scope.

For example, TypeScript emits class methods (and getters-setters) as properties on the constructor's prototype. The reason for assigning methods to the prototype instead of each new instance is of course efficiency - you don't want to make new closures every time you create an instance. If the private fields were only available within the constructor body, the methods could not access them.

Reflect.ownKeys will list all keys (string or symbol or otherwise) of an object. It's not clear from the article if it can be excluded via an ES5 definition with `enumerable: false`; perhaps so? In that case, this is pretty cool.
I'm not sure of the JS implementation in particular, but it wouldn't surprise me. Keep in mind that the security/encapsulation features of many languages (private, final, const, etc.) can be bypassed using reflection, especially in "more dynamic" languages like Javascript.
There's also Object.getOwnPropertySymbols()

At any rate, Symbol properties aren't meant to be a bullet-proof way to hide private properties. They just reduce the default public API surface of an object by suppressing its internals.

It seems they can be used as a little safer enums, like :keywords in clojure. It can come handy if you want to add method to object that nobody will overwrite by accident.

But clojure was designed around :keywords, while js was designed around strings as property names. Adding another possibility now complicates everything.

I don't know if I like it.

I think you'd add methods nobody can overwrite using Object. defineProperty(), which is an ES5 thing.
Seems like it could be used as a way to lazily copy objects; a solution like this could make a lot of sense if, say, a server had entire objects instantiated and the client only needs to know they exist, but not what each object contains.
Unique (derived) tokens that are not equal to any other value.

For example, symbols let you implement a map data structure without having to worry about shadowing built-in properties of the object.

If you have Symbols, you also have Map and WeakMap which don't have that problem.