-
Notifications
You must be signed in to change notification settings - Fork 17.7k
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
proposal: Go 2: type switch case statements with multiple interface types should produce an intersection of methods #65031
Comments
I'm not sure that
|
@rittneje valid concerns, for this to be backward compatible returns of values which "just" implement the interface must be valid Simplified example: |
This is an interesting idea, and I do like how it echoes the way that constrained type parameters on generic functions end up supporting the intersection of methods that all of the allowed types support. I don't think there's any existing precedent in Go for the compiler silently generating a unnameable type, which means there are likely to be unexpected consequences of adding the first case of that. However, it does seem to me that doing this for interface types in particular is plausible because the language already allows implicit conversions between compatible interface types anyway, and so there's a reasonable set of things you could do with a value of one of these unnameable types that would still be useful. Making the following legal also seems like an implication of this proposal: type Example interface {
Method(i int) *types.Func
NumMethods() int
Underlying() types.Type
String() string
}
func UsesExample(ex Example) {
// ...
}
func Elsewhere(typ reflect.Type) {
switch newTyp := typ.(type) {
case *types.Named, *types.Interface:
// The automatically-created anonymous interface type
// is compatible with the named type Example, so
// no explicit conversion is required here.
UsesExample(newTyp)
}
} This is admittedly contrived because it would've been equivalent to write The fact that the "hidden" anonymous interface type would be visible through I am also a little concerned about how this automatically-generated anonymous type would appear in error messages when there's a type mismatch. It seems like we'd need to spell it out in full as |
One consequence of this change is that the call to z.F would be to an interface method call that has no declaration:
This would affect the (That said, the solution to #51259 and the analogous feature for methods, both of which seem like important generics features, will pose the same problem, and other language changes sometimes do break go/types API incompatibility.) |
I kinda feel like the underlying request is for |
@timothy-king I'm not set on the specific syntax, your syntax would also work. |
This looks like a subset of https://go.dev/issue/57644. |
I know the domain of go/types well, and while I agree that it is sometimes a nuisance to have to type out a throwaway interface type for a type switch, it is not very common, and it is exceedingly rare to need the interface to have more than one method. So I don't think there's a compelling need for a language change here. |
Given there is a workaround, and a more general proposal in #57644, this is a likely decline. Leaving open for four weeks for final comments. |
No change in consensus, so declined. |
Go Programming Experience
Experienced
Other Languages Experience
Go, Rust, TS/JS, C/C++, Python
Related Idea
Has this idea, or one like it, been proposed before?
No.
Does this affect error handling?
No.
Is this about generics?
No.
Proposal
Currently newTyp in the case statement has the
types.Type
type:Proposed change:
Instead it should have the type of an implicitly generated interface, which consists of the intersection of all methods both types have in common.
In current go this would be:
I know that this helps people who analyze go code, because it allows for more concise type switches, which are very common when analyzing code. There are probably other use cases, like parsers, anytime when there is a type switch to narrow down the type and then calling a method on that type.
It would also be nice if this works for the intersection of struct fields, but I think additional work on generics is required first, in order to have a similarly low cost "syntactic sugar" implementation.
Language Spec Changes
This new behavior would need to be documented under https://go.dev/ref/spec#Type_switches
Informal Change
In case statements with multiple types, the switched type is of an interface which contains the intersection of both types methods.
Is this change backward compatible?
Strictly speaking no, because the output of
reflect.TypeOf
may change, however I don't think this will break programs.reflect.TypeOf
could pretend that the implicitly generated interface doesn't exist and tell the caller thattyp
is of typetypes.Type
, then it would be backward compatible.Before the change (this is real code from here):
After the change:
Orthogonality: How does this change interact or overlap with existing features?
This should make go code easier to read and write.
Would this change make Go easier or harder to learn, and why?
Easier, I think the current behavior is non intuitive.
Cost Description
This requires someone to implement the transformation in the compiler. Also gopls needs to be updated to know that this is something the compiler does. And someone needs to update the language spec.
Changes to Go ToolChain
gopls, compile
Performance Costs
Some small compile time cost, no run time cost.
Prototype
This code:
Could be converted to:
The text was updated successfully, but these errors were encountered: