Composite constraints

We know that if the Sum[T](...T) T function is going to be able to handle all, possible numeric types that:

  • inlining all those possibilities would be poorly written code
  • the any keyword will not work because the addition operator is invalid for the any type

Instead something like any is needed but that is constrained to just the possible numeric types. The solution is to create a composite constraint using the following syntax:

  1. // Numeric expresses a type constraint satisfied by any numeric type.
  2. type Numeric interface {
  3. uint | uint8 | uint16 | uint32 | uint64 |
  4. int | int8 | int16 | int32 | int64 |
  5. float32 | float64 |
  6. complex64 | complex128
  7. }
  8. // Sum returns the sum of the provided arguments.
  9. func Sum[T Numeric](args ...T) T {
  10. var sum T
  11. for i := 0; i < len(args); i++ {
  12. sum += args[i]
  13. }
  14. return sum
  15. }

Now the function Sum[T Numeric](...T) T can be used to sum values of any numeric type (Go playground):

  1. func main() {
  2. fmt.Println(Sum([]int{1, 2, 3}...))
  3. fmt.Println(Sum([]int8{1, 2, 3}...))
  4. fmt.Println(Sum([]uint32{1, 2, 3}...))
  5. fmt.Println(Sum([]float64{1.1, 2.2, 3.3}...))
  6. fmt.Println(Sum([]complex128{1.1i, 2.2i, 3.3i}...))
  7. }

The above program will compile and print the expected result:

  1. 6
  2. 6
  3. 6
  4. 6.6
  5. (0+6.6i)

Yet…let’s rexamine the following statement:

the function Sum[T Numeric](...T) T can be used to sum values of any numeric type

Is this really true? What about a type definition with an underlying type of int? In fact, that will not work as Sum[T Numeric](...T) T is currently written (Go playground):

  1. // id is a new type definition for an int64
  2. type id int64
  3. func main() {
  4. fmt.Println(Sum([]id{1, 2, 3}...))
  5. }

In fact the above program fails to compile. The next section explores the tilde ~ operator and how it can be used to further expand how constraints are expressed.


Next: Tilde ~