Private fields, public worries

Filip Białek
VirtusLab
Published in
7 min readNov 7, 2019

JavaScript — as a language — grows larger every year. It shouldn’t be surprising that the process of introducing new features to JS can cause a lot of turmoil. Programmers usually held strong opinions about their tools so it’s rather expected that any change to JS spec will be discussed relentlessly by developers. However, some of the changes proposed by TC39 Committee (body responsible for EcmaScript standard) arouse more resistance than others. A proposal that recently caused a lot of negative reactions was class fields proposal that — among others — tries to add to the language private class fields.

Giovanni Battista Bracelli from “Bizzarie di varie Figure” 1624

PROPOSAL

The current form of the candidate is a result of combining two earlier proposals — viz. one introducing public fields and another proposing private class fields. Consequently, the proposal allows us to declare class fields inside the class body but outside constructor function and — besides — it introduces two kinds of class fields — public (properties) and private. Let’s see some code.

class BankAccount {  balance = 0;  #clientId = ‘xyz’;
deposit = (amount) => { this.balance += amount; }; getId = () => this.#clientId;}

balance is public property. It can be accessed and modified outside of a class body. As you can see, we don’t need a constructor function. We can just declare public properties directly. This part of the proposal is rather uncontroversial. It saves us a few keystrokes to omit constructor function. I guess most developers using React are familiar with this syntax. Quite often you can see a class component defined like this:

class Widget extends React.Component {  state = {    open: false  };  toggle = () => {    this.setState(prevState => ({    open: !prevState.open    })  };  render...}

Here the initial state is defined as a public class field. However, it can be easy to overlook that also toggle is using the new class fields syntax. In other words, toggle isn’t a class method, it is an instance property (so, yes, every instance of the Widget has its own copy of toggle).

This is all well and good but there is a white elephant in the room. Yes, I am thinking about # character. This is not a comment, this is not a CSS id selector. This is how you declare private fields according to the proposal. You can access such a field only inside the class body. To do so you can write either this.#myPrivateField or use shorthand and omit this altogether by writing #myPrivateField.

PRIVACY

But what exactly does it mean that a field is private? Let’s give voice to the authors of the proposal:

It means that private fields are purely internal: no JS code outside of a class, can detect or affect the existence, name, or value of any private field of instances of said class without directly inspecting the class’s source unless the class chooses to reveal them.
[PRIVATE SYNTAX FAQ]

So not only we cannot access private fields outside of a class, we also shouldn’t be able to know that certain class has any private fields whatsoever. Trying to access such a private field shouldn’t result in an error or an exception. If that was the case we would know that a class has private fields with such and such a name. The proposal ensures that no part of the program outside of a class can refer in any way to private fields declared inside the class. The language makes it impossible. If you are outside a class you can only interact with public fields (a.k.a. properties).

CRITICISM

Ok, now we know what is being proposed. Let’s see what arguments are used to criticize the proposal.

UGLINESS

I guess the most frequent reaction to private fields syntax boils down to three words (usually with exclamation mark): ‘it’s ugly’. TC39 committee, of course, know it:

Most people said please stop the proposal, we can’t stand the sigil

[TC 39 January 26, 2017 Meeting Notes]

Indeed, it is ugly. Some people claim that aesthetics it’s rather a matter of taste than a matter of facts. But here I’d say that we have a simple aesthetic fact: # sigil syntax is ugly.

But is it valid counter-argument at all? In my opinion, it is. Even if there are reasons for using such a syntax (and there are, and we will discuss them) ugliness/beauty of new syntax should be taken into account. An ugly language is harder to use than a beautiful language. The proposed syntax is unintuitive. It looks like a comment or CSS id selector. The authors of the proposal are aware that their choice isn’t an obvious one.

No one came out and said, # is the most beautiful, intuitive thing to indicate private state. Instead, it was more of a process of elimination

[PRIVATE SYNTAX FAQ]

So why # sigil was chosen? The most natural candidate for marking private field would be — of course — keyword private. Why it wasn’t chosen by the committee? It wouldn’t be a problem to declare a private field in this way. But how would you access it?

class Widget {  private mySecret = '1234';}const obj = new Widget();obj.mySecret = '5689';

In Java, it would result in a compile error. But JavaScript isn’t compiled language. Runtime error? Really terrible idea to break your program because someone wants to add a property to an object. Moreover, it would mean — at least according to proposal authors — that such a field isn’t really private because its effects are visible from outside the class.

HARD PRIVACY

Many JS devs are uncomfortable with another aspect of the proposal, namely: obstinance of proposal champions on hard privacy. Hard privacy means that there is no way to access or even detect the existence of a field outside the class. The term is opposed to soft privacy which gives users the possibility to access such fields. Of course such escape hatches should be annoying and difficult but nonetheless, access would be possible. However, one of the goals of the proposal is to guarantee that private fields in JS will be hard private.

Either it’s fully “hard private” — inaccessible and unobservable — or else it’s fully public.

[Github discussion]

Such a statement is obviously false. If that was a common understanding of the concept of privacy, authors of the proposal wouldn’t need to make a distinction between soft and hard privacy. Soft privacy shouldn’t be call privacy at all…

There are at least two counter-arguments that can be used against hard privacy. Firstly, in many popular languages, private fields are soft private (in Java for example you can access private fields using reflection). So not only the committee is forcing unexpected syntax to achieve private fields, it uses quite an unusual notion of privacy as well. Of course, such an argument doesn’t explain what is wrong with hard privacy. It just tells us that this construct is not very common.

Secondly, one can argue that hard privacy is rather impossible to achieve anyway. You can always use toString method to directly see which fields were declared as private. And if toString would fail — don’t worry — there would be a dozen different ways of checking the source code of any library, that you are using at the moment. So yes, you can detect the existence of a private field — one way or another…

But why the committee insists so strongly on hard privacy in the first place? To answer that question we need to realize that the main beneficiaries of the proposal are authors of libraries. As an author of a library you don’t want users of your lib to have access to anything but public api. Even detectability of soft private fields could be a problem — argue members of the committee — because it makes possible for users of a library to write conditionals where detecting certain private fields has some important consequences.

So here hard privacy seems to be really useful because it should give the possibility to completely hide implementation details. But it does not. If they want — users always find ways to access such fields. One can say that users who will try those ways are breaking the rules, therefore libraries authors should not care about such users. But the same can be said about users that access soft private fields or even properties prefixed with _. Such developers also break a contract with the author of a library.

But there is another argument for hard privacy — in my opinion much more compelling. Soft privacy is already accessible in the language via symbols. There is no need for syntactic sugar for such a construct because there is really nothing complicated in using symbols for soft private fields. Hence if somebody doesn’t need hard privacy they can just use symbols for soft privacy.

There are many other doubts about the proposal. One can certainly mention that debugging can be harder with hard-private fields, also monkey patching will be much more challenging. However I’d like to discuss only one more objection raised against the proposal. This objection has more general nature so let me sum up the previous discussion. I would say that rationales behind decisions that were taken to formulate the proposal (hash sigil, hard privacy) are sound. They are not ultimately conclusive but at least they are logical and coherent with proposal goals. Probably other ways of introducing hard private fields to the language (and you can check them in github issues discussions) would result in a much more convoluted solution. Sigil # gives us nice symmetry between declaring a private field and accessing it. Hard privacy offers a coherent and reliable mechanism of hiding implementation details.

But maybe we just don’t need private fields in the language? Maybe introducing private fields to a language in which class keyword is nothing more than syntactic sugar isn’t such a good idea. Because after all JS classes are just syntactic sugar over prototype inheritance.

In my opinion, such an addition to a language could produce a language that is unnecessary complicated. By this, I don’t want to say that new syntax is so unintuitive that developers will struggle with it. Proposed syntax, albeit ugly, is quite simple. What may become not so simple is language as a whole. After all, learning a language is not the same as learning its syntax.

Thanks to Michał Stawski, Wojciech Karaś and Krzysztof Borowski.

--

--