Hacker News new | ask | show | jobs
by chriswarbo 4192 days ago
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

2 comments

>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.