-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Property name collisions. #9
Comments
I agree that it'd be great for the language to help with this situation. I think whatever the solution, it should apply equally to classes and mixins. Symbols are a great primitive to use here, and already partially solve this problem. Imagine a class that defines symbol-named properties: const foo = Symbol('A.foo');
const bar = Symbol('A.bar');
class A {
static foo = foo;
static bar = foo;
[foo] = 1;
[bar]() { console.log(this[foo]); }
} We can subclass it, safe from naming collisions: class B extends A {
[A.bar]() { console.log(this[A.foo], 'from B'); }
} The computed property name syntax is even pretty nice here - I don't think it needs much improvement. The real issue is that declaring manually namespaced names is cumbersome, so people are less likely to do it. One option I like to simplify declaring namespaced properties is to use decorators. @littledan has previously proposed something like class A {
@namespaced foo = 1;
@namespaced bar() { console.log(this[A.foo]); }
}
If we had this mechanism for classes, it would naturally apply to mixins. This would need to be a separate proposal. It's also one reason I really like the built-in decorators proposal, because for static analysis we need there to be a standard common understanding of decorators that can change the shape of classes. (cc @DanielRosenwasser and @rbuckton for that point). |
The problem is, we can't expect everyone to use symbols (or Also having to use the namespaces for every property access would be less ergonomic, when most of the time it wouldn't be needed. It's only the collision case where it is really needed. I believe there needs to be a way to look up based on string key names. I wonder how it can be done. |
I want to write simple classes like these, which work great standalone with syntax we all are familiar with: class Size {
x = 1
y = 2
log () { console.log('size: ', this.x, this.y) }
}
let size = new Size(10, 20)
size.log() class Position {
x = 3
y = 4
log () { console.log('position: ', this.x, this.y) }
}
let position = new Position(30, 40)
position.log() Now suppose we had a language feature to mix classes together (instead of class-factories): class Thing extends Position, Size {
log() {
// how?
console.log('thing size:', ???)
console.log('thing position:', ???)
}
} Maybe, by default, classes used in this fashion would be automatically namespaced in their own code, and the subclass could explicitly use the name space. Without new syntax, it might be like this: class Thing extends Position, Size {
log() {
console.log('thing size:', Object.get(this, Size, 'x'), Object.get(this, Size, 'y'))
console.log('thing position:', Object.get(this, Position, 'x'), Object.get(this, Position, 'y'))
}
}
new Thing().log()
// output:
// thing size: 1 2
// thing position: 3 4 This isn't as ergonomic as regular properties, but no new syntax, and only needed in limited cases where collisions happen. Most of the time it isn't needed. There could be a rule, that by default accessing a key grabs the value from the first class (namespace) in the class Thing extends Position, Size {
log() {
console.log('thing size:', this.x, this.y)
console.log('thing position:', this.x, this.y)
}
}
new Thing().log()
// output:
// thing size: 1 2
// thing position: 1 2 The less ergonomic syntax can be used only when needed, for the occluded properties: class Thing extends Position, Size {
log() {
console.log('thing size:', this.x, this.y)
console.log('thing position:', Object.get(this, Position, 'x'), Object.get(this, Position, 'y'))
}
}
new Thing().log()
// output:
// thing size: 1 2
// thing position: 3 4 |
If we had a feature like the previous comment, with rules around property access priority, then the properties and implied namespaces would be statically analyzable. |
Because I know someone is going to ask, here are thoughts on how namespacing might work at a very high level: Based on how JavaScript works, where all properties are on If by default mixed class properties are namespaced internally by the engine, and the engine knows which class a method is from, then it can access properties namespaced as needed when it knows there are collisions, and it can know about collisions because it can know (statically) every class that I think this is vague, but with enough ideas to spark some imagination on how it could work. |
I have a basic implementation with tests here:
The syntax is of course not native, so it is like import {multiple} from 'lowclass'
class One {...}
class Two {...}
class Three {...}
class Thing extends multiple(One, Two, Three) { ... } A native-like syntax would be easy to make with Babel. It's simple at the moment with various edge cases to be solved (some things difficult to achieve with only existing Proxy features). TODO:
|
Could this be a part of the class that receives mixins? mixin Vector2 {
x = 0
y = 0
divide() {...}
multiple() {...}
}
class Point with Vector2 {
constructor() {
this.x // accesses as if it's defined by the class.
mixins.Vector2.x // accesses `x` defined by the mixin.
this[Symbol.mixins].Vector2.x // Alternative without new syntax.
}
} But honestly not sure if this useful, I believe the best way to handle this is to just throw an error if there are conflicting names. Do you have any world-examples where it could be useful - I'm curious. |
It'd be great if a feature at the language level includes a way to call (or rename) properties or methods that have the same name in different mixins applied to one class, as well as having options for how to handle the diamond problem. Any thoughts on those issues?
The text was updated successfully, but these errors were encountered: