Type switch: https://golang.org/ref/spec#Switch_statements
The example below is from above link. It illustrates the status quo:
Given an expression x of type interface{}, the following type switch:
switch i := x.(type) {
case int:
printInt(i) // type of i is int
case bool, string:
// This scenario can be made more useful.
printString("type is bool or string") // type of i is type of x (interface{}) <---------
default:
printString("don't know the type") // type of i is type of x (interface{})
}
It would be good if when 2 case types are specified in case:, the common methods are available for i, rather than it being a generic interface{ } which renders the type switch to have minimal value.
Stated another way: i behaves like the intersection of the types listed in the particular case, with regards to what methods are available to it inside the type switch case.
A key assumption is that under the proposal, if the programmer does not want this new behaviour, then they must separate out the types in the cases. This allows the programmer to use current functionality or the new enhanced functionality.
This has been raised before and rejected (can't find the issue). The only addition that I see here is that this proposes the type of i to be interface{}.
You can do it yourself:
package main
import (
"fmt"
"reflect"
)
type B bool
func (B) M(){}
type S struct{}
func (S) M(){}
func main() {
var i interface{} = B(true)
switch x := i.(type) {
case interface {M()}:
fmt.Println("type of x:", reflect.TypeOf(&x).Elem())
// type of x: interface { M() }
}
switch x := i.(type) {
case B, S:
fmt.Println("type of x:", reflect.TypeOf(&x).Elem())
// type of x: interface {}
}
}
@agnivade The contrary. You are pointing out the status quo. I'm suggesting it can be made more useful, particularly with generics support.
Ah my bad. Didn't read it carefully. Must have confused it with some similar issue.
I'm probably missing something, but isn't this already working?
package main
type I interface {
foo()
bar()
}
type J interface {
bar()
baz()
}
type T struct{}
func (T) foo() {}
func (T) bar() {}
func (T) baz() {}
type U struct{}
func (U) bar() {}
func (U) baz() {}
func main() {
v := J(T{})
switch x := v.(type) {
case nil:
case T, U:
x.bar()
}
}
@cznic not quite.
In clauses with a case listing exactly one type, the variable has that type; otherwise, the variable has the type of the expression in the TypeSwitchGuard.
Your example merely works because x will be of type J because v is of type J. It wouldn't work if v was interface{}, for example.
OP, on the other hand, is asking for x to be of an interface type whose method set is the intersection of all the method sets of the types mentioned in the case, regardless of the type in the switch guard.
But that requires giving up compile time type safety. I dit not assume that's the proposal. In such case, this works today:
package main
type I interface {
foo()
bar()
}
type J interface {
bar()
baz()
}
type T struct{}
func (T) foo() {}
func (T) bar() {}
func (T) baz() {}
type U struct{}
func (U) bar() {}
func (U) baz() {}
func main() {
v := interface{}(T{})
switch x := v.(type) {
case nil:
case T, U:
x.(J).bar()
}
}
I see where I made a mistake again...
@dominikh states my intentions more eloquently and more clearly. This can be implemented without generics support.
However, my intentions can be generalised further with generics support: Even common fields can be accessed inside that particular multi-type case. That would be even more useful but it is probably stretching what the big boys like @robpike would accept.
I think I understand what it would mean for the language to create an interface that is the intersection of the method sets of all the types in the case. But I don't understand the initial example. It uses the types bool and string, and those types have no methods. Does this proposal change any aspect of the behavior of the initial example?
I like that Go's type system is extremely simple, and this would (essentially) introduce a union type which can only be used in a switch statement... If we're going to introduce this, we should instead introduce true union types.
If you're proposing this to be used for method unions, why not just use interfaces? https://play.golang.org/p/N0fDa_0Vj_y
If you're proposing this to allow for the following
n := 5
switch num := val.(type) {
case int, int32, int64:
fmt.Println(n + num)
}
This is not possible as n would be an int, and int + int32 shouldn't be able to compile.
If you're suggesting we should allow this at least for untyped constants, I do not like that idea. The idea of "untyped constants" is already confusing enough and we do not need to perpetuate more special cases for untyped constants.
The example above was just taken from the spec. The proposal would have no effect on bool, string
What would be an example where this would be useful and could not be done with interfaces? Interfaces are designed for stuff like this
The type switch will create an artificial interface that consists of the common methods. See @dominikh comment.
@deanveloper : This would be an intersection type, not a union type. The type of the variable declared in the type switch would have an interface type whose methods are the intersection of the method sets all the types in the case statement.
This would be an intersection type, not a union type
My bad, oops
The type switch will create an artificial interface that is the common methods. See @dominikh comment.
Okay that makes sense, although IMO defining an interface for the specific purpose would be better.
Plus a big issue is that this encourages really large case statements, as all of your handling for the variables would need to happen _inside of the case_. This is because the variables (which have type Type1&Type2&... at compile time) cannot be passed into functions which take Type1 or Type2 etc.
That is unless we generalize this approach and instead just implement intersect types, then we could create functions which take Type1&Type2&... as an argument, which would greatly help the previous problem.
This proposal requires the compiler to create a new intersection type for each type switch case with multiple types. I guess that for types that are not interface types, the compiler would construct the intersection of the method sets of those types, and create an interface type with those methods.
If all you care about is the methods, then you can already do this by constructing the intersection type itself. If you want to use types like bool and string as in the initial example, then presumably the intersection type is interface{}, but it's not clear why that would be desirable.
Given that we can already write an intersection type, this proposal doesn't seem to carry its weight.
Most helpful comment
I like that Go's type system is extremely simple, and this would (essentially) introduce a union type which can only be used in a
switchstatement... If we're going to introduce this, we should instead introduce true union types.If you're proposing this to be used for method unions, why not just use interfaces? https://play.golang.org/p/N0fDa_0Vj_y
If you're proposing this to allow for the following
This is not possible as
nwould be anint, andint + int32shouldn't be able to compile.If you're suggesting we should allow this at least for untyped constants, I do not like that idea. The idea of "untyped constants" is already confusing enough and we do not need to perpetuate more special cases for untyped constants.