Instantiations
…
A partial type argument list cannot be empty; at least the first argument must be present. The list is a prefix of the full list of type arguments, leaving the remaining arguments to be inferred. Loosely speaking, type arguments may be omitted from “right to left”.
func apply[S ~[]E, E any](s S, f func(E) E) S { … } f0 := apply[] // illegal: type argument list cannot be empty f1 := apply[[]int] // type argument for S explicitly provided, type argument for E inferred f2 := apply[[]string, string] // both type arguments explicitly provided var bytes []byte r := apply(bytes, func(byte) byte { … }) // both type arguments inferred from the function arguments
Let’s demonstrate this by refering to the example from yesterday. We had the following function:
func sum[T ~int | ~float64, R ~int | ~float64](x ...T) R
And could not infer the return type:
fmt.Println(sum(1,2,3)) // cannot infer R
This means we must explicitly provide the return type in the call, as illustrated:
fmt.Println(sum[int, int](1, 2, 3)) // Success!
But since the return type comes second in the type parameter list, it means we must also explicitly provide the type for T
, even though it could be inferred. There’s no way to skip over that first type argument:
sum[_, int](1, 2, 3) // Nope
sum[..., int](1, 2, 3) // Nuh-uh
sum[?, int](1, 2, 3) // No way!
Of course we could re-order the type parameters in the function, if we really care about this:
func sum[R ~int | ~float64, T ~int | ~float64](x ...T) R
sum[int](1, 2, 3) // now this is valid
One last note on instantiations before our next topic tomorrow:
For a generic type, all type arguments must always be provided explicitly.
In other words, there’s no way to infer the type parameters for a generic type.
type X[T any] []T
x := X{1, 2, 3} // cannot use generic type X[T any] without instantiation
y := X[int]{1, 2, 3} // valid
Quotes from The Go Programming Language Specification Version of August 2, 2023