An example is rolling your own discriminated union:
type Avariant struct {
a int
}
type Bvariant struct {
b string
}
type SumChoice int
const (
SumA SumChoice = 0
SumB SumChoice = 1
)
type Sum struct {
type SumChoice
A *Avariant
B *Bvariant
}
sumA := Sum{choice: SumA, A: &{a: 7} }
sumB := Sum{choice: SumB, B: &{b: "abc"} }
func foo (x Sum) {
switch(x.choice) {
case SumA: {
fmt.Printf("Value is %d", x.A.a)
}
case SumB: {
fmt.Printf("Value is \"%s\"", x.B.b)
}
}
}
As usual with Go, it's ugly and verbose and error prone, but it just about works.The previous poster was probably thinking of something similar but using interface{} (equivalent to Object or void*) instead of Sum, and then pattern matching using an explicit type switch:
func foo (x interface{}) {
switch actual := x.(type) {
case AVariant: {
fmt.Printf("Value is %d", actual.a)
}
case BVariant: {
fmt.Printf("Value is \"%s\"", actual.b)
}
}
}
This is slightly less risky and has less ceremony, but it's also less clear which types foo() supports based on its signature. Since Go has only structural subtyping [0] for interfaces, you would have to add more boilerplate to use a marker interface instead.[0] In Go, a type implements an interface if it has functions that match (name and signature) the interface functions. The name of the interface is actually irrelevant: any two interfaces with the same functions are equal. So if you declare an interface MyInterface with no methods, it is perfectly equivalent to the built-in `interface{}`: any type implements this interface, and any function which takes a MyInterface value can be called with any type in the program.
On the first approach, with "error prone" you mean the tag could be incorrect, right? (or even have an impossible variant with 0 or multiple variants set).
type Sum struct {
type SumChoice
A Avariant
B Bvariant
}
This is what I meant by saying that it depends on exactly what you mean by "sum types".Still, I think most people don't worry so much about efficient packing of sum types, and instead the safety and convenience factors are more important. Of course, YMMV based on exact usage.
I'm not in anyway claiming that Go supports sum types. Someone just asked how they may be emulated, and I don't think it should be surprising that emulation has an overhead.