Description
Author background
-
Would you consider yourself a novice, intermediate, or experienced Go programmer?
Experienced. -
What other languages do you have experience with?
Java; Scala; C/C++; Python
Related proposals
-
Has this idea, or one like it, been proposed before?
I don't believe so. -
Does this affect error handling?
No -
Is this about generics?
Yes- If so, how does this relate to the accepted design and other generics proposals?
This is a proposal for an improvement to the inference procedure to reduce redundancy.
- If so, how does this relate to the accepted design and other generics proposals?
Proposal
- What is the proposed change?
Today, whenever a generic argument is passed to a generic function, you must specify the concrete type at the call-site and repeat this type for each argument.
// declarations
type Generic[T any] struct { t T }
func Foo[T any](x Generic[T]) { }
// current usage
Foo[string](Generic[string]{t: "hello"})
This proposal is to modify the type inference procedure to allow the types specified at the callsite to be inferred for arguments.
// proposed usage
Foo[string](Generic{t: "hello"})
The utility of this change is more easily seen when passing generic functions to other generic functions.
// declarations
func Generic[T any](t T) { }
func Foo[T any](f func(T)) { }
// current usage
Foo[string](Generic[string])
// proposed usage:
Foo[string](Generic)
This should improve readability by allowing users to make calls to generic libraries more tersely than they would need to today. It would also enhance the utility of generic libraries which seek to offer functional options to their users for configuration.
- Who does this proposal help, and why?
Authors and users generic libraries, who want to be able to pass generic type parameters to generic functions without repeating themselves unnecessarily.
The main driver for this proposal is this function call, which is rather verbose (snippet below).
// call-site:
withOptions[string](t, Opts[string](), func(t *testing.T, opts []pipelines.OptionFunc[string]) {
// ...
})
// declarations involved:
type optionMap[T any] map[string][]pipelines.OptionFunc[T]
func withOptions[T any](t *testing.T, opts optionMap[T], f func(*testing.T, []pipelines.OptionFunc[T]))
- Please describe as precisely as possible the change to the language.
Whenever a generic argument X[T]
is seen in a call to a generic function func Foo[T](X[T])
, any concrete types seen in the parameter for calls involving Foo
are applied during type inference for the arguments. This allows usage like Foo[string](X)
to compile, whereas today, usage like Foo[string](X[string])
is required.
See the proposed change to the language spec below.
- What would change in the language spec?
At first blush, adding a step to the current type inference procedure, before step 1 seems like it would avoid the duplication involved.
- If the generic type is an argument to a generic function; all values from the function's parameter list are included in the parameter map.
Other portions of the spec may need to be modified as well to take this change into account.
-
Please also describe the change informally, as in a class teaching Go.
-
Is this change backward compatible?
Yes; since this change would just make syntax which is required today unnecessary. -
Show example code before and after the change.
-
Before
See playground link containing the below code.
func Foo[T any](f func(T)) {
// ...
}
func Bar[T any](t T) {
// ...
}
type AStruct[T any] struct {
ts []T
}
func Returns[T any]() AStruct[T] {
return AStruct[T]{ts: make([]T, 2)}
}
func Foo2[T any](f func() AStruct[T]) {
// ...
}
func main() {
Foo[string](Bar[string])
Foo2[string](Returns[string])
}
- After
// new main method:
func main() {
Foo[string](Bar)
Foo2[string](Returns)
}
-
Orthogonality: how does this change interact or overlap with existing features?
This change has the potential to interact with downstream stages of the type inference procedure, and this should be considered carefully. -
Is the goal of this change a performance improvement?
No.
Costs
-
Would this change make Go easier or harder to learn, and why?
Unclear. On the one hand, one could argue that this may may Go somewhat easier to learn because having to specify the types in every case is a bit confusing; especially when there can only be one answer. Another argument may be that having to place explicit types everywhere is clearer, even when redundant. -
What is the cost of this proposal? (Every language change has a cost).
Time taken to implement and potentially unforeseen consequences of modifying type inference. -
How many tools (such as vet, gopls, gofmt, goimports, etc.) would be affected?
A non-zero number of them. I am unsure. -
What is the compile time cost?
Type inference could become slightly more expensive. -
What is the run time cost?
None. -
Can you describe a possible implementation?
Before beginning type inference for a generic argument to a generic function argument; add any concrete types from the parameter list of the generic function to the parameter map. Track whenever this is done so that the compiler can also ensure that code is generated for these arguments.
- Do you have a prototype? (This is not required.)
No.