Sobes.tech
Senior

Cos'è la somma di tipi e come implementarla in Go?

sobes.tech AI

Risposta dell'AI

La somma di tipi (o unione etichettata, unione disgiunta) è un tipo di dato che può assumere il valore di una delle diverse opzioni predefinite. A differenza del prodotto di tipi (strutture), dove il valore contiene tutti i campi contemporaneamente, la somma di tipi contiene solo uno dei valori possibili.

In Go, non c'è supporto diretto per le somme di tipi come in linguaggi come Haskell o Rust (enum). La somma di tipi può essere emulata in diversi modi:

  1. Interfacce e asserzione di tipo (Type Assertion):

    Creare un'interfaccia che tutte le possibili varianti implementano. Il valore della somma di tipi sarà questa interfaccia, e per determinare la variante concreta si usa l'asserzione di tipo o type switch.

    package main
    
    import "fmt"
    
    // Shape - interfaccia che rappresenta la somma di tipi
    type Shape interface {
    	Area() float64
    }
    
    // Circle - una delle varianti
    type Circle struct {
    	Radius float64
    }
    
    func (c Circle) Area() float64 {
    	return 3.14 * c.Radius * c.Radius
    }
    
    // Rectangle - un'altra variante
    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 {
    		// Uso di type switch per determinare la variante
    		switch v := s.(type) {
    		case Circle:
    			fmt.Printf("Cerchio con raggio %.2f, area: %.2f\n", v.Radius, v.Area())
    		case Rectangle:
    			fmt.Printf("Rettangolo con dimensioni %.2f x %.2f, area: %.2f\n", v.Width, v.Height, v.Area())
    		default:
    			fmt.Println("Forma sconosciuta")
    		}
    	}
    }
    
  2. Strutture con flag booleani (usate raramente):

    La struttura contiene campi per tutte le varianti possibili e flag booleani per indicare quale variante è attiva. È insicuro e difficile da mantenere.

    package main
    
    import "fmt"
    
    type Result struct {
    	Value int  // campo per risultato di successo
    	Err   error // campo per errore
    
    	IsValue bool // flag che indica se Value è attivo
    	IsErr   bool // flag che indica se Err è attivo
    }
    
    func Process(input int) Result {
    	if input > 0 {
    		return Result{Value: input * 2, IsValue: true}
    	}
    	return Result{Err: fmt.Errorf("numero negativo: %d", input), IsErr: true}
    }
    
    func main() {
    	res1 := Process(10)
    	if res1.IsValue {
    		fmt.Printf("Risultato: %d\n", res1.Value)
    	} else if res1.IsErr {
    		fmt.Printf("Errore: %v\n", res1.Err)
    	}
    
    	res2 := Process(-5)
    	if res2.IsValue {
    		fmt.Printf("Risultato: %d\n", res2.Value)
    	} else if res2.IsErr {
    		fmt.Printf("Errore: %v\n", res2.Err)
    	}
    }
    
  3. Strutture con valori nulli per campi non attivi (spesso usate per Optional/Result):

    La struttura contiene campi per tutte le varianti, ma solo uno di essi sarà non nullo. Funziona bene con puntatori o tipi con un valore "null" ben definito.

    package main
    
    import "fmt"
    
    // Option - emulazione di Some/None del tipo opzionale
    type Option struct {
    	Value *int // Non nullo se c'è un valore
    }
    
    // None crea un valore Option nullo
    func None() Option {
    	return Option{}
    }
    
    // Some crea un Option con valore
    func Some(val int) Option {
    	return Option{Value: &val}
    }
    
    func main() {
    	opt1 := Some(10)
    	if opt1.Value != nil {
    		fmt.Printf("Valore: %d\n", *opt1.Value)
    	} else {
    		fmt.Println("Nessun valore")
    	}
    
    	opt2 := None()
    	if opt2.Value != nil {
    		fmt.Printf("Valore: %d\n", *opt2.Value)
    	} else {
    		fmt.Println("Nessun valore")
    	}
    }
    

Il modo più idiomatico e sicuro per emulare somme di tipi in Go è usare interfacce e type switch. Questo garantisce che solo una delle varianti sia presente e fornisce sicurezza di tipo nel lavorarci.