tc39 / proposal-grouped-and-auto-accessors

Grouped Accessors and Auto-Accessors for ECMAScript
https://tc39.es/proposal-grouped-and-auto-accessors
MIT License
54 stars 5 forks source link

Support changing the backing field used by auto-accessors #14

Open zaygraveyard opened 1 year ago

zaygraveyard commented 1 year ago

Reopening https://github.com/tc39/proposal-decorators/issues/511 here as suggested by @pzuraq in this comment.

The README of the decorators proposal stats that the getter and setter defined by the auto-accessors "default to getting and setting a value on a private slot.", so why not allow the use of a different backing field?

The suggestion

Allow accessor to take the backing field name as parameter: accessor(fieldName) accessorName.

accessor(#x) x = 1; would be equivalent to accessor x = 1; except that with the former #x would be accessible syntactically in the class.

The following example would be valid:

class C {
  accessor(#x) x = 1;

  method() {
    this.#x = 456;
  }
}

The backing field can also be public for those who want to avoid private class fields:

class C {
  accessor(_x) x = 1;
}

The above would roughly desugar to:

class C {
  _x = 1;
  get x() {
    return this._x;
  }
  set x(val) {
    this._x = val;
  }
}

This would probably also solve #10 as being an alternative to the private getter and setter syntax.

Other alternative

An alternative that would more resemble C# would be (or similar):

class C {
  #x = 1;
  accessor x { get as #x; set as #x; }
}

PS: This is my first time contributing to discussions on TC39 proposals 😅

ljharb commented 1 year ago

That seems like a nice addition, altho i don’t see the value in allowing public properties to be set here - if you want a public property then what’s the point of get/set functions?

zaygraveyard commented 1 year ago

While it may appear that the sole purpose of get/set functions is to provide public access to private properties, I assure you that this is not the case. Accessors have a broader functionality as they facilitate the abstraction of a class or object's internals, including private-by-convention properties, private language properties, and dynamic properties (such as computed properties).

Accessors have several common use cases, such as:

  1. Read-only properties
  2. Lazy-evaluated properties
  3. Input validation when setting a property
  4. Emitting events when the property value changes

These use cases are applicable to both private class fields and private-by-convention public class fields.

Private class fields are intentionally more restricted than public ones (for instance, they are incompatible with use cases requiring "protected" or "package" visibility). Therefore, in certain scenarios where private class fields cannot be utilized, a private-by-convention approach is more suitable.

EDIT:

It occurred to me that I might have misunderstood the point you were trying to make. If you're saying that accessor(_x) x = 1 is mostly the same as x = 1, that would be true only if no decorator is used as the former is an accessor property and the later is a data property.

Please let me know if I'm still missing something.

zaygraveyard commented 1 year ago

Furthermore, allowing non-private names might simplify expanding auto-accessors to work on object literals, as the README states:

Under Consideration: We may consider expanding auto-accessors to work on object literals in the future, however the necessary private name semantics are currently not defined for object literals.

ljharb commented 1 year ago

If an accessor’s backing is a public property then the abstraction is leaky, since anyone can get at the value without going through the getter, or mutate it without going through the setter.

zaygraveyard commented 1 year ago

Absolutely, that's why I called it private-by-convention and it's how we used to do private properties be for private class fields. But as I've mentioned before, some use cases cannot make use of private class fields as they are too restrictive. Until a better alternative exists, private-by-convention are the only option.

Aside: Since one of those restrictions is their incompatibility with proxies (https://github.com/tc39/proposal-class-fields/issues/106), might this be remedied in a future proposal or is it a non-starter?

ljharb commented 1 year ago

I think it’s a nonstarter - it’s not just proxies, it’s anything that depends on the identity of a receiver. One simply can’t wrap something one doesn’t own and expect to be able to have identical semantics.

zaygraveyard commented 1 year ago

Thank you for the clarification 😄

zaygraveyard commented 1 year ago

To provide further clarification, I hold the belief that public field backed auto-accessors should be allowed. Denying their usage would restrict the adoption of auto-accessors to that of private class fields, which would be unfortunate considering the benefits of this feature.