When function instantiations can be inferred

January 2, 2024

A quick update on my livestream: It’s on hold until February, as my family and I made a bit of a last-minute trip to visit family for the month of January, so I won’t be in my studio for a while.


Instantiations

When using a generic function, type arguments may be provided explicitly, or they may be partially or completely inferred from the context in which the function is used. Provided that they can be inferred, type argument lists may be omitted entirely if the function is:

In all other cases, a (possibly partial) type argument list must be present. If a type argument list is absent or partial, all missing type arguments must be inferrable from the context in which the function is used.

// sum returns the sum (concatenation, for strings) of its arguments.
func sum[T ~int | ~float64 | ~string](x... T) T { … }

x := sum                       // illegal: the type of x is unknown
intSum := sum[int]             // intSum has type func(x... int) int
a := intSum(2, 3)              // a has value 5 of type int
b := sum[float64](2.0, 3)      // b has value 5.0 of type float64
c := sum(b, -1)                // c has value 4.0 of type float64

type sumFunc func(x... string) string
var f sumFunc = sum            // same as var f sumFunc = sum[string]
f = sum                        // same as f = sum[string]inferred:

So the TL;DR; here is, if it’s possible to tell what types are needed from the context, you don’t need to provide them explicitly. For functions, at least, this is going to be the common case. Most often, we reference generic functions when they are called.

So when is this not enough?

Well here’s a contrived example:

func sum[T ~int | ~float64, R ~int | ~float64](x ...T) R {
	var total T
	for _, i := range x {
		total += i
	}
	return R(total)
}

In this modified sum function, the result type is distinct from the input type, meaning one could request the float64 value of summing two int values. In this example, the result type cannot be inferred:

fmt.Println(sum(1,2,3)) // cannot infer R

So we need to provide the explicit types when we call the function:

fmt.Println(sum[int, int](1, 2, 3)) // Success!

See it in the playground

Quotes from The Go Programming Language Specification Version of August 2, 2023


Share this

Direct to your inbox, daily. I respect your privacy .

Unsure? Browse the archive .

Related Content


Partial type argument lists

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.


Instantiations

Instantiations A generic function or type is instantiated by substituting type arguments for the type parameters. Instantiation proceeds in two steps: Each type argument is substituted for its corresponding type parameter in the generic declaration. This substitution happens across the entire function or type declaration, including the type parameter list itself and any types in that list. After substitution, each type argument must satisfy the constraint (instantiated, if necessary) of the corresponding type parameter.


Generic functions as operands

Operands … An operand name denoting a generic function may be followed by a list of type arguments; the resulting operand is an instantiated function. So first off, recall that an operand may be a function literal: var x = func() { /* ... */ } Or a variable that represents a function: var F = func() { /* ... */ } var f = F // <--- `F` is a variable that represents a function Or another expression that evaluates to a function: