Sobes.tech
Senior

Wat is de som van types en hoe implementeer je deze in Go?

sobes.tech AI

Antwoord van AI

De som van (of getagde unie, disjuncte unie) is een gegevenstype dat een van de meerdere vooraf gedefinieerde opties kan aannemen. In tegenstelling tot het product van types (structuren), waarbij de waarde alle velden tegelijk bevat, bevat de som van types slechts één van de mogelijke waarden.

In Go is er geen directe ondersteuning voor sommen van types zoals in talen als Haskell of Rust (enum). De som van types kan op verschillende manieren worden geëmuleerd:

  1. Interfaces en type assertion:

    Maak een interface die alle mogelijke varianten implementeert. De waarde van de som van types zal deze interface zijn, en om de specifieke variant te bepalen wordt type assertion of type switch gebruikt.

    package main
    
    import "fmt"
    
    // Shape - interface die de som van types representeert
    type Shape interface {
    	Area() float64
    }
    
    // Circle - een van de varianten
    type Circle struct {
    	Radius float64
    }
    
    func (c Circle) Area() float64 {
    	return 3.14 * c.Radius * c.Radius
    }
    
    // Rectangle - een andere variant
    type Rectangle struct {
    	Width, Height float64
    }
    
    func (r Rectangle) Area() float64 {
    	return r.Width * r.Height
    }
    
    func main() {
    	shapes := []Shape{Circle{Radius: 5}, Rectangle{Width: 3, Height: 4}}
    
    	for _, s := range shapes {
    		// Gebruik van type switch om de variant te bepalen
    		switch v := s.(type) {
    		case Circle:
    			fmt.Printf("Cirkel met straal %.2f, oppervlakte: %.2f\n", v.Radius, v.Area())
    		case Rectangle:
    			fmt.Printf("Rechthoek met afmetingen %.2f x %.2f, oppervlakte: %.2f\n", v.Width, v.Height, v.Area())
    		default:
    			fmt.Println("Onbekende vorm")
    		}
    	}
    }
    
  2. Structuren met boolean flags (zelden gebruikt):

    De structuur bevat velden voor alle mogelijke varianten en boolean flags om aan te geven welke variant actief is. Dit is onveilig en moeilijk te onderhouden.

    package main
    
    import "fmt"
    
    type Result struct {
    	Value int  // veld voor succesvol resultaat
    	Err   error // veld voor fout
    
    	IsValue bool // vlag die aangeeft of Value actief is
    	IsErr   bool // vlag die aangeeft of Err actief is
    }
    
    func Process(input int) Result {
    	if input > 0 {
    		return Result{Value: input * 2, IsValue: true}
    	}
    	return Result{Err: fmt.Errorf("negatief getal: %d", input), IsErr: true}
    }
    
    func main() {
    	res1 := Process(10)
    	if res1.IsValue {
    		fmt.Printf("Resultaat: %d\n", res1.Value)
    	} else if res1.IsErr {
    		fmt.Printf("Fout: %v\n", res1.Err)
    	}
    
    	res2 := Process(-5)
    	if res2.IsValue {
    		fmt.Printf("Resultaat: %d\n", res2.Value)
    	} else if res2.IsErr {
    		fmt.Printf("Fout: %v\n", res2.Err)
    	}
    }
    
  3. Structuren met null-waarde voor inactieve velden (vaak gebruikt voor Optional/Result):

    De structuur bevat velden voor alle varianten, maar slechts één daarvan zal niet null zijn. Dit werkt goed met pointers of types die een duidelijke "null"-waarde hebben.

    package main
    
    import "fmt"
    
    // Option - emulatie van Some/None uit het optionele type
    type Option struct {
    	Value *int // niet null als er een waarde is
    }
    
    // None creëert een null-waarde Option
    func None() Option {
    	return Option{}
    }
    
    // Some creëert een Option met waarde
    func Some(val int) Option {
    	return Option{Value: &val}
    }
    
    func main() {
    	opt1 := Some(10)
    	if opt1.Value != nil {
    		fmt.Printf("Waarde: %d\n", *opt1.Value)
    	} else {
    		fmt.Println("Geen waarde")
    	}
    
    	opt2 := None()
    	if opt2.Value != nil {
    		fmt.Printf("Waarde: %d\n", *opt2.Value)
    	} else {
    		fmt.Println("Geen waarde")
    	}
    }
    

De meest idiomatische en veilige manier om sommen van types in Go te emuleren, is het gebruik van interfacessen type switch. Dit zorgt ervoor dat slechts één variant aanwezig is en biedt typeveiligheid bij het werken ermee.