Make private properties and proxy work together (RETRACTED)

EDIT 2: I retract my proposal. I need to get my act together first if at all.

EDIT: I made a mistake (wrongly assumed that proxy failures with private properties are silent). Not yet sure how to fix this mistake. Sorry.

This is my first post here. Please excuse me. I did a little bit of research before posting about an idea that jumped into my head today. It seems that there was some discussion, but nothing about this idea.

It's about private properties with the #-syntax and proxies and how to make them work together in a sensible way.

  1. Classes can declare how they want their private properties handled.

  2. There should be three simple defaults:
    a. Warning if an object with private properties gets proxied.
    b. Failure with an exception if an object with private properties gets proxied.
    c. Transparent proxying that preserves private properties.

If the class declares nothing, by default a warning should be printed (a). One should be able pass an option to Proxy to force an early error (b), except if the class chooses (c) or a more complicated handling.

I deliberately left syntax and details open.

EDIT: A complication: what should be done if an private property is proxied that contains objects with different handling of proxying. Let's say if the inner object chooses (b) and the outer property (c). Which one wins?

I am sure the devil is in the details but I hope a solution can be worked out.

A proxy can’t access closed-over variables, and thus it can’t access private fields.

Any attempt to change the latter must also find a way to change the former, and dynamic scoping is off the table, so that seems to indicate that this isn’t a solvable problem.

Thank you!

To clarify: From the viewpoint of the Proxy it is indeed impossible but a class should have the authority. After all it's the class that has access to its private properties.

The class can certainly expose it - via public methods. Proxies mustn’t be able to access private things without the class making them public, though.

I am not talking about exposing properties but deciding how they are handled by proxies. A class could allow a proxy to transparently proxy private properties as well.

In my proposal I made a mistake. It has been a long time ago that I worked with private properties and proxies because I knew they don't work together and I assumed some things that are wrong. Sorry.

But this doesn't change the idea that a class could decide what to do when proxying private properties.

It might be easier to discus if you could present some concrete use cases, to compare how they would currently be approached

I read this two years ago and suddenly an idea popped in my head today, what about letting classes declare how they want their private properties handled?

I don't think the specification can say if a warning should be printed. This warning may also be incorrect. Code can already creates proxies around classes/built-in-types with private state (e.g. Map) with an appropriate handler to ensure methods will be dispatched with the original target as the receiver.

That's the mistake I made. Perhaps I should restart from scratch with my proposal?

Sorry for that.

With language design where there are multiple options, there is always the solution of "do all of them and let the user decide". While this sounds like the solution that would please everyone it comes at the cost of being a more complicated language to both learn and to implement.

I do think this would be easier to discuss with a concrete use case to structure things around.

EDIT: I don't have a problem in my current programming just now. I don't need Q&A, sorry.

I found another way how to wrap something with private properties and proxying that. Here a transcript.

>> class A { #a = "private a"; get() { return this.#a }; set(a) { this.#a = a }}
undefined
>> a = new A
Object { #a: "private a" }
>> a.get()
"private a"
>> a.set("changed a")
undefined
>> a.get()
"changed a"
>> b = new Proxy(a, {})
Proxy { <target>: {}, <handler>: {} }
>> b.get()
Uncaught TypeError: cant access private field or method: object is not the right class
>> class X { a = new A }
undefined
>> x = new X
Object { a: {} }
>> x.a.get()
"private a"
>> y = new Proxy(x, {})
Proxy { <target>: {…}, <handler>: {} }
>> y.a.set("changed a in x") 
undefined
// wow! y is a proxy of x which indirectly contains a private property
>> x.a
Object { #a: "changed a in x" }
y.a.get()
"changed a in x"
// seems to work!

Sorry to be a bother. Let me give a summary:

It is true that you can't proxy things with private properties, but you can proxy things that wrap them.

Right?

EDIT: I retract that. It works tautologically, this means y.a doesn't use a proxy. Sorry. I am confused. Please trash my proposal.

I still think you have a lovely and perfectly valid point. Right now JS is like two languages. One language has the freedom to proxy, and the other language has the freedom to make things private. I don't know I guess you might call the languages Proxascript and Privatescript.

I think it's a bummer that you kind of have to pick one of those two capabilities to have support for. Given such a strange choice, I think the most cogent philosophy is not to use either feature in production-quality code.

1 Like

As a note, without using either proxies or class private properties you can accomplish the combination of facading and privacy, which obviously go great together. E.g.

// not exported, thus private
const actuals = new WeakMap();

export class Facade {
  constructor(actual) {
    actuals.set(this, actual);
  }

  get state() {
    return actuals.get(this).state;
  }
}

class Actual {
  constructor(state) {
    this.state = state;
  }
}

Should I retry? I would need to research very carefully, at least more than for my first attempt, and that takes time.

ProxaScript and PrivateScript really need a truce.

PrivateScript tells ProxaScript: You may proxy me! However only if my object did an opt-in! And even then, my private properties are absolutely holy! They are never ever visible!

ProxaScript tells PrivateScript, okay, here is the Symbol. If this symbol is set to true, I proxy you. And I don't look at your private properties. I just pass them around and while I do that I close my eyes.

Okay enough of this colorful metaphor. I am not sure whether this is workable. Let me think about it.

1 Like

Another way is using closures. Have a let binding in the constructor and then add acessors or functions to the this object.

Have a let binding in the constructor and then add accessors or functions to the this object.

Does that work if you want your methods to be defined only once? I'd think you'd need to redefine each accessor or method for each instance so that the correct internal instance variable would be closed over.

If instead you just close over an actuals or privateStates WeakMap that problem goes away.

For the record, of PrivateScript and Proxascript, it's Proxascript that doesn't really seem to exist: JS already lacks a fundamental freedom to proxy (even without private members) because proxies are a leaky abstraction

Proxies were never meant to be able to trap identity. Before private fields you had 2 ways of implementing private state: closures and WeakMap side tables using the identity of the object as key. The former is intrinsically compatible with all proxy usages because each method is unique per object and captures the context. The latter is not because methods are shared between instances and rely on the identity of the receiver. Private fields being class based where behavior is shared between instances followed the model of WeakMap based private state.

Btw, proxies are not intrinsically incompatible with private fields, it's just that the proxy handler needs to implement a facade / membrane that doesn't leak the identity of the receiver. If you expose any code to both the proxy's own identity and the proxy's target, then you risk confusion if that code relies on object identity for anything.

1 Like