-
Notifications
You must be signed in to change notification settings - Fork 27
Preference for static {}
block to perform privileged static initialization
#23
Comments
I agree that a |
To clarify, I'm not advocating removal of |
Another thing you can do with a |
Also to clarify on the intended semantics of
I've probably forgotten a few things and may augment this comment as I think of them. |
Also a note on |
Absolutely: let A, B;
{
let friend_A_x_get;
let friend_A_x_set;
A = class A {
#x;
static {
friend_A_x_get = a => a.#x;
friend_A_x_set = (a, value) => a.#x = value;
}
}
B = class B {
constructor(a) {
const x = friend_A_x_get(a); // ok
friend_A_x_set(a, value); // ok
}
}
} |
Example of this hack: let C;
{
let foo;
let z; // block-scoped "static private" state
C = class C {
#x; // instance private state
static init = (() => { // static privileged initialization
foo = function() { } "static private" behavior
z = new C();
z.#x = ...;
})();
};
delete C.init; // bad because optimizing compilers often de-optimize on delete
} And one more approach using decorators: function static_init(member) {
assert(member.placement === "static");
assert(member.kind === "method");
assert(typeof member.key !== "privatename"); // cannot `delete` a private name
const _init = member.descriptor.value;
member.descriptor.value = undefined;
member.finisher = klass => {
_init.call(klass);
delete klass[member.descriptor.key];
};
return member;
}
let C;
{
let foo;
let z; // block-scoped "static private" state
C = class C {
#x; // instance private state
@static_init
static _init() { // static privileged initialization
foo = function() { } "static private" behavior
z = new C();
z.#x = ...;
})();
};
} While a decorator seems like a fair way to implement this, I'd rather have a syntactic block to better support static analysis in tooling scenarios (such as definite assignment analysis). |
NOTE: I've added the following to #23 (comment), above:
|
I find the parallel between the constructor and the static block quite interesting. Pretending that we don't have field syntax for a moment: class C {
constructor() {
this.x = 0;
this.y = 0;
}
static {
this.a = 1;
this.b = 1;
}
} The parallel here is beautiful and makes me far less interested in either static fields or public fields. |
see my comment at #24 (comment) |
I think that at this level description, you could say: the static {} block is evaluated as if it was a static concise method of the class that is invoked on the constructor object. |
The one hole I see is a reasonable way to synthesize a instance "private method" that has correct let privateInstanceHelper;
class C extends B {
#priv; //a private instance slot
m() {privateInstanceHelper.call(this)}
static {
let HomeBinder = {
__proto__ = this.prototype.__proto__;
instanceHelper() {
this.#priv; //works when called with a C instance as this.
super.foo() //super call correctly follows C's (original) prototype chain.
}
}
privateInstanceHelper = HomeBinder.instanceHelper;
}
} If private instance helpers that can correctly do super calls in an common/important enough use case (it isn't clear that it actually is) then perhaps that would partially justify including "private methods" as currently proposed. |
@rbuckton I find your argument very compelling. It seems that static blocks are a core primitive that is useful to get at the scope which classes add, and are expressive for many purposes. For example, lexical declarations in class bodies could be explained as syntactic sugar on top of static blocks. I wonder if, at this point, with difficult tradeoffs every way we turn, it'd be best to start off minimal when it comes to these static class features. That minimal proposal would be static field declarations and static blocks, which give programmers expressiveness, at the cost of some awkwardness, for dealing with the scopes of private names introduced in the Stage 3 class fields proposal. As follow-on proposals, we can consider static private methods and fields, lexical declarations in classes, private name declarations outside of classes, or other things. Some of the discussion on this thread seems to be about whether other Stage 3 proposals are well-motivated. For private methods and accessors, I responded here. For public static and instance field declarations, you can find the motivation in @jeffmo's excellent explainer (search for "Why"). For details, @rbuckton 's suggestions at #23 (comment) seem great to me and more sensible than what I was previously imagining. I'd probably go with those. Just for fun, here's some alternatives we might consider:
|
I don't actually advocate this in this form, to be clear; I'd only be behind it if there were some syntax which didn't have the leaking concern (e.g. if it did not use |
The best I could come up with, and it's unacceptably bad, is To clarify, this is just if we wanted to leak declarations. I think the normal static block syntax that @rbuckton proposed, |
Overlapping with label syntax seems… suboptimal. |
I've created a more formal proposal for this feature at https://github.com/rbuckton/proposal-class-static-block. |
Currently if you want "static private" state and "static private" behavior we are proposing the following design:
While this allows for "static private" behavior to have privileged access to instance private state and "static private" state, the only mechanism we have for privileged initialization of "static private" state is lazy-initialization (or a hacky use of a static public field initializer):
What we need is a mechanism for privileged static initialization:
This leads me to believe that, while
local
functions can give us behavior with privileged access to lexically scoped private names, many scenarios still need a clear mechanism for privileged static initialization.However, if we have privileged static initialization I can instead write my above example as follows:
As such, I feel that privileged static initialization is a better mechanism for providing access to privileged behavior as it aligns both "static private" state and "static private" behavior and reduces overall complexity.
The text was updated successfully, but these errors were encountered: