-
Notifications
You must be signed in to change notification settings - Fork 213
Should typedefs be usable as classes (when their definition is a class) #89
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
Comments
For reference, Kotlin allows all of these. open class A<T>() {
companion object {
fun someStaticMethod() {print("Got it")}
}
constructor(x : Int) :this() {print(x)}
}
typealias MyA = A<Int>;
typealias MyGA<T> = A<T>;
class B : MyA() {}
fun main() {
MyA();
MyA(2);
MyGA<Int>();
MyGA<Int>(2);
MyA.someStaticMethod();
MyGA.someStaticMethod(); |
Supporting it would be consistent with the motivation of the feature. If typedef ScaffoldMap = Map<ScaffoldFeatureController<SnackBar, SnackBarClosedReason>, SnackBar>;
ScaffoldMap map = Map<ScaffoldFeatureController<SnackBar, SnackBarClosedReason>, SnackBar>(); And if you are not using Flutter style guide and use OTOH, this would actually make the code concise: ScaffoldMap map = ScaffoldMap(); Whether you are using types or |
Ooh, this is an interesting one. Here's my ten-minute take: new MyA();
new MyA.named(); Yes. It felt a little weird to me, but, like Yegor notes, this is like half of the main value proposition of the whole feature. MyA.staticMethod(); I think we basically have to, yes. It would be profoundly confusing if we didn't since, with optional new, the distinction between static method and constructor is increasingly an implementation detail. Users would be really surprised by: MyA(); // OK.
MyA.named(); // OK.
MyA.staticMethod(); // Not OK. It does feel kind of strange because new MyGA<int>();
new MyGA<int>.named(); Yup. I think users will expect it. I can see it being practically useful as a way to partially apply type arguments to a generic type: typedef StringMap<V> = Map<String, V>;
new StringMap<int>(); // Etc. MyGA.staticMethod(); I think this is fine too. |
I consider it to be a questionable idea to allow But note that the error-prone verbosity that we are fighting occurs in a complex list of type arguments, it doesn't have anything to do with the length of the name of the class vs. the name of the type alias. Both names would be in the same name space, so if the class name has to be |
I don't think allowing statics on instantiated generic types will block us from later allowing access to those type arguments. Currently it is a compile-time error if a static member refers to a class type variable, so no existing static code can depend on the value of the type arguments (and hence cannot detect whether they are discarded or not). There is just no other way to actually access a static member than I do worry about use-cases like |
It is a bit odd, dropping the type arguments. But the counter argument feels really strong to me. If renaming a class |
Taking a look at Swift type aliases, they can be non-generic and generic just like Dart's, but missing type arguments are taken to be an abbreviation for passing all type arguments as-is (so I haven't yet found any evidence to confirm or reject the assumption that it's possible to use a type alias that denotes a parameterized type for accessing static members (and it seems to take a number of steps to get started using Swift). |
About the actual topic of this thread, I agree that we get pretty good coverage if we allow the raw type alias name to stand in for the raw class name in a static member access. That could be because we i2b the actual type arguments and then ignore them, or it could be because we recognize that situation and allow the type alias to denote the namespacy usage of the class name. Note that the latter interpretation implies that we do not ignore the type arguments, we just allow class NewName<X, Y> {
...
void staticMethod() {...}
}
typedef OldName<X, Y> = NewName<X, Y>;
main() {
OldName.staticMethod();
} This approach might be less of an anomaly. It would fit the use case where a class is renamed, but we do not change it from non-generic to generic. In contrast we would need to allow the actual type arguments to be ignored if we also want to support the following: // BEFORE.
class OldName {
...
void staticMethod() {...}
}
main() {
OldName.staticMethod();
}
// AFTER.
class NewName<New, Type, Arguments> {
...
void staticMethod() {...}
}
typedef OldName = NewName<WithSome, Default, TypeArguments>;
main() {
OldName.staticMethod(); // Error, unless we can ignore type arguments.
} So we may be able to keep the anomaly small if we only support static accesses for type aliases which (1) have no type parameters and pass no type arguments, or (2) have a type parameter list and passes it on to the type on the right hand side. The only nasty case is the hybrid where we have a non-generic type alias that passes some type arguments on to a generic class on the right hand side (explicitly or via i2b). But there could also be other situations worth considering, e.g.: class C<X, Y, Z> {
...
void staticMethod() {...}
}
typedef NormalC = C<String, int, Map<String, int>>;
main() {
NormalC.staticMethod();
} Now consider the situation where we decide that class NormalC implements C<String, int, Map<String, int>> {...} This will break |
Swift allows statics on generics classes with access to the type argument. They do not allow access through the raw type without inferring a type. They do allow access through type aliases.
(using http://online.swiftplayground.run/). |
In other words, Swift considers a static member to be a member of the instantiated generic class ("that has received its type arguments already"), except that they don't want to handle a separate copy of the state for each actual type argument list. So even though that seems like a nice and coherent starting point, they also managed to make static members a bit messy. Surprise surprise. ;-) |
Just to make @lrhn's summary above concrete, example swift code: class A<T> {
static func ss() {}
class func cc() {}
// Not allowed in a generic static var v = ""
}
typealias B<T> = A<T>;
typealias C = A<Int>;
func test() {
// Not allowed A.ss;
// Not allowed A.cc;
A<Int>.ss();
A<Int>.cc();
B<Int>.ss();
B<Int>.cc();
C.ss();
C.cc();
} |
CL 81414 was updated such that the rules are maximally permissive (in particular, it is allowed to invoke static methods using expressions like |
Closing: We are using the maximally permissive approach specified by CL 81414. |
This issue is for discussion of the open questions around whether type names (see proposal) introduced by generalized typedefs should be usable in contexts expecting class names when the RHS of the typedef is itself a class type. For example:
The text was updated successfully, but these errors were encountered: