Repository: go
Issue: proposal: spec: sum types based on general interfaces
Labels: LanguageChange,Proposal,generics,LanguageChangeReview

This is a speculative issue based on the way that type parameter constraints are implemented.  This is a discussion of a possible future language change, not one that will be adopted in the near future.  This is a version of #41716 updated for the final implementation of generics in Go.

We currently permit type parameter constraints to embed a union of types (see https://go.dev/ref/spec#Interface_types).  We propose that we permit an ordinary interface type to embed a union of terms, where each term is itself a type.  (This proposal does not permit the underlying type syntax `~T` to be used in an ordinary interface type, though of course that syntax is still valid for a type parameter constraint.)

That's really the entire proposal.

Embedding a union in an interface affects the interface's type set.  As always, a variable of interface type may store a value of any type that is in its type set, or, equivalently, a value of any type in its type set implements the interface type.  Inversely, a variable of interface type may not store a value of any type that is not in its type set.  Embedding a union means that the interface is something akin to a sum type that permits values of any type listed in the union.

For example:

```Go
type MyInt int
type MyOtherInt int
type MyFloat float64
type I1 interface {
    MyInt | MyFloat
}
type I2 interface {
    int | float64
}
```

The types `MyInt` and `MyFloat` implement `I1`. The type `MyOtherInt` does not implement `I1`.  None of `MyInt`, `MyFloat`, or `MyOtherInt` implement `I2`.

In all other ways an interface type with an embedded union would act exactly like an interface type. There would be no support for using operators with values of the interface type, even though that is permitted for type parameters when using such a type as a type  parameter constraint. This is because in a generic function we know that two values of some type parameter are the same type, and may therefore be used with a binary operator such as `+`. With two values of some interface type, all we know is that both types appear in the type set, but they need not be the same type, and so `+` may not be well defined. (One could imagine a further extension in which `+` is permitted but panics if the values are not the same type, but there is no obvious reason why that would be useful in practice.)

In particular, the zero value of an interface type with an embedded union would be `nil`, just as for any interface type. So this is a form of sum type in which there is always another possible option, namely `nil`. Sum types in most languages do not work this way, and this may be a reason to not add this functionality to Go.

As an implementation note, we could in some cases use a different implementation for interfaces with an embedded union type.  We could use a small code, typically a single byte, to indicate the type stored in the interface, with a zero indicating `nil`.  We could store the values directly, rather than boxed.  For example, `I1` above could be stored as the equivalent of `struct { code byte; value [8]byte }` with the `value` field holding either an `int` or a `float64` depending on the value of `code`.  The advantage of this would be reducing memory allocations.  It would only be possible when all the values stored do not include any pointers, or at least when all the pointers are in the same location relative to the start of the value.  None of this would affect anything at the language level, though it might have some consequences for the `reflect` package.

As I said above, this is a speculative issue, opened here because it is an obvious extension of the generics implementation. In discussion here, please focus on the benefits and costs of this specific proposal. Discussion of sum types in general, or different proposals for sum types, should remain on #19412 or newer variants such as #54685. Thanks.