From my naive perspective, they seem like an easy win, and are 100% more important to me than generics.
type foo either {
int
string
*bar
baz
}
... var f foo = ....
switch v := f.(type) {
case int:
// v has type int
case string:
..... type thing1 int
type thing2 int
type foo either {
thing1
thing2
}
var f foo = …
switch v := f.(type) {
case thing1:
data = int(v)
case thing2:
data = int(v)
…
and frankly that's a bit gross.I think `select` would be a better basis for dispatching than switch as it already supports getting data out of stuff, and it better represents the linear top-to-bottom dispatching.
type foo struct (
kind Kind
data int
}
Then you switch on kind and you use the dataThe type switch is useful when you have to consume the actual type, without runtime errors for type conversions
No, I'm pointing out that a proper sum type can (and often does) need that. It doesn't mean those are the only members.
> The type switch is useful when you have to consume the actual type, without runtime errors for type conversions
My point is that the type switch is garbage when multiple variants can have the same associated type.
With destructuring pattern match constructs you'd be binding variables to "inner" members of the sum type.
I do understand that. I'm not so sure it's so important, compared to Just being able to say that a box can contain either A or B or C.
Interfaces are great when you don't care what does a box contain as long as it quacks like a duck.
Sometimes though you need to carry around one out of N types of something and currently all you can do is to use an interface{} and deal with the possibility of a runtime error if somebody breaks the invariant.
It absolutely is, even more so because of Go's interface. The difficulty of properly discriminating between interfaces extending one another is a reason the FAQ gives for rejecting union types.
> compared to Just being able to say that a box can contain either A or B or C.
I'd argue that this is by far the lesser use case, and furthermore trivially and completely subsumed by the alternative.
> Sometimes though you need to carry around one out of N types of something and currently all you can do is to use an interface{} and deal with the possibility of a runtime error if somebody breaks the invariant.
And sometimes you need to carry around one of N values some of which have overlapping contents and currently all you can do is get bent.
Edit: true explanation here https://golang.org/doc/faq#variant_types
In practice, that means that there's just not much overlap between their semantics. Sum types let you say, "A Widget is either a Foo or a Bar, but nothing else." Interfaces give you no way to set boundaries like that. They say, "A Widget is anything that declares itself to be a Widget." And then you can declare Widgets Foo and Bar, sure, but anyone else can come along later and create Baz and Bof.
Interfaces, on the other hand, place restrictions on behavior. You say, "A Widget must support these operations," and, if Foo and Bar are Widgets, they must support those operations. Sum types don't let you do that. A sum type Widget with elements Foo and Bar places zero restrictions on what Foo and Bar look like. They could be literally anything.
The question, "What would happen if the elements of a variant type were themselves interfaces?" leaves me wondering if the authors' consideration of variant types consisted of taking a cursory look at Scala, which does (confusingly and hackily) co-opt Java's inheritance mechanisms in its implementation of sum types. Which does lead to some serious language warts. There are plenty of other languages which have both interfaces (or typeclasses) and sum types implemented more independently, though, and it does not typically lead to confusion.
That last paragraph is also somewhat bothersome, and makes me think once again that this response is more of an offhanded dismissal than a considered answer. The full question is essentially, "Why don't you implement this feature that would greatly increase the language's type safety?" and the response is, "Because you don't need it. See, if you just abandon type safety, what you can do is..."
I suspect that the real answer, even if the FAQ authors or the people who designed the language don't realize it, is that generics are practically (if not technically) a precondition for an algebraic type system. You could implement one without generics, but it wouldn't be very useful.
Now with the new generics proposal, it seems they have hacked up some crude closed interface types, which are something like union types. :/
Secondly, they think interfaces cover many of the need of variant types and they don't want some non-orthogonal feature.
Since it's an interface{} it has no behaviour, the only thing you can do is a type assert / type switch.
By extension, you could add some methods: interface(A,B,C){Foo(bool) int} This fails statically unless A, B, and C implement said interface. If they do, then this interface has exactly the same semantics as the normal Go interfaces, except that only types A, B and C can be assigned to it (and not type D even if it implements Foo(bool) int