Skip to content
This repository has been archived by the owner on Jan 25, 2022. It is now read-only.

Array syntax for private properties #74

Closed
shdwjk opened this issue Jan 19, 2018 · 12 comments
Closed

Array syntax for private properties #74

shdwjk opened this issue Jan 19, 2018 · 12 comments

Comments

@shdwjk
Copy link

shdwjk commented Jan 19, 2018

Reading the FAQ here talking about why this['#x'] doesn't work made me wonder if it would make sense to add an array syntax to the proposal like this:

this#['x']

Are there other ways under the proposal to dynamically access a private property?

@shdwjk shdwjk changed the title Array syntax for private members Array syntax for private Properties Jan 19, 2018
@shdwjk shdwjk changed the title Array syntax for private Properties Array syntax for private properties Jan 19, 2018
@adrianheine
Copy link

This proposal is actually opposed to the idea of having dynamic access to private fields, see FAQ.

@jridgewell
Copy link
Member

see FAQ

That doesn't quite fit. The example in this case:

class Dict extends null {
  #data = something_secret;
  add(key, value) {
    this[key] = value;
  }
  get(key) {
    return this[key];
  }
}

(new Dict).get('#data');

Here, the get and set are expecting to expose public data, not private. Anything that would expose the private #data field must be rejected from this proposal. However, the OP's idea is to make the syntax explicit:

class Dict extends null {
  #data = something_secret;
  add(key, value) {
    this#[key] = value;
  }
  get(key) {
    return this#[key];
  }
}

(new Dict).get('data');

Here, the class's author has explicitly allowed access to private internals, and there's no way to mix public/private access in the same locations. You either opt into only private, or you only get public.

I'm not super in favor of it (I don't have a use case yet), but I'm not outright against it.

@adrianheine
Copy link

You're right, the example is no perfect fit. I think the general point that dynamic access to private fields does not align well with the idea expressed in this proposal still stands.

@shdwjk
Copy link
Author

shdwjk commented Jan 19, 2018

I don't have a perfect use case either, just a solution to the case of implicit access to private data.

I could see it being used in a purely get format (similar to C#'s property syntax for public get and private set), and possibly in some sort of calculated/validated set:

class Dict extends null {
  #data = something_secret;
  add(key, value) {
    /* do something explicit with attempted changes to private data */
  }
  get(key) {
    return this#[key];
  }
}

(new Dict).get('data');

I suppose there might be some issue with returning a reference to something private rather than it's value, but that's more of an implementation detail and not unique to array access.

@ljharb
Copy link
Member

ljharb commented Jan 19, 2018

if obj#[x] is valid, I’d expect the static syntax to be obj#.x

@bakkot
Copy link
Contributor

bakkot commented Jan 19, 2018

Some background:

JavaScript uses objects in at least three different ways: as arrays, as maps, and as records.

For arrays and maps, computed property access makes a lot of sense: in the case of arrays you're often accessing properties with an iterator variable, as in for (let i = 0; i < array.length; ++i) array[i] += 1;, and in the case of maps you're using an arbitrary string (or symbol) as a key, as in let cache = Object.create(null); function cachedCompute(k) { return k in cache ? cache[k] : cache[k] = compute(k); }.

That's not the case for records. For a record, like { value: 0, done: false }, you have a fixed, known list of keys. In those cases computed access makes less sense, and you generally know exactly which property you want: hence the static obj.key syntax. Sometimes you might want one of several things, but even then I think it's clearer to do cond ? obj.foo : obj.bar rather than obj[cond ? 'foo' : 'bar'].


OK, with that context: private fields as defined in this proposal are very much in the record model above. From the perspective of code in the class, an instance of a class has a fixed, finite list of private fields which are declared up front, one at a time, in the class body. The names of all of these fields are statically known, and no new fields can be added. Because of this, referring to a private field which isn't defined is a static error, rather than being a runtime error or giving you undefined.

(Sidebar: the Dict sample code in the comments above mine wouldn't work at all: because fields have to be declared up front, the only key which would work is "data". Separately, this syntax breaks the mental model that the # is conceptually part of the name of the field; in the class above, I would encourage people to think of it as a class with a private field #data, rather than data.)

Since private fields are record-like, I don't think it makes sense for the language to provide dynamic access to them.

@shdwjk
Copy link
Author

shdwjk commented Jan 19, 2018

Does the proposal allow for iterating over private properties of a class?

@bakkot
Copy link
Contributor

bakkot commented Jan 19, 2018

No.

@shdwjk
Copy link
Author

shdwjk commented Jan 19, 2018

Lacking parity with the fundamental behavior of public properties seems unfortunate, but I've only happened on this proposal today, so haven't been able to fully grok the implications.

You said:

The names of all of these fields are statically known, and no new fields can be added. Because of this, referring to a private field which isn't defined is a static error, rather than being a runtime error or giving you undefined.

Coming from C++, I can certainly appreciate the benefit of static checking over runtime. However, why is that a requirement for private properties? (I didn't see anything in the FAQ about it.) This seems like another place where parity with the existing property functionality would be more intuitive.

@bakkot
Copy link
Contributor

bakkot commented Jan 19, 2018

Private fields being statically declared is a requirement because that's the whole model for their privacy: they're like lexical declarations, such that only code which has visibility of them can even refer to them.

And since they're statically declared, there's not nearly as much value in iterating over them. But adding that ability would require even more syntax.

More generally, most languages I'm aware of which provide records do not provide a way of iterating over the keys of those records. I think the only reason it's something that it's very important to be able to do with public properties of objects in JavaScript is because objects function as arrays and maps in addition to records. But I don't think it's desirable for private properties to be usable in an array-like or map-like way; if you want a private map, put a Map in a private field. I think that treating private properties as record-like is by far the most consistent mental model, and is something we should encourage by their design.

@jridgewell
Copy link
Member

I think that treating private properties as record-like is by far the most consistent mental model, and is something we should encourage by their design.

Agreed.

@shdwjk
Copy link
Author

shdwjk commented Jan 19, 2018

That seems like mixing implementation details with requirements, and blending the supposition that "as records" is the right way to use them with the false reality that it's the only way to use them.

That said, having a property that is a map gives the same functionality you could get from iterating the private properties directly with only a little indirection, so there is a solution for anyone on the unfortunate side of your doctrinal line in the sand. =D

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants