Go introduced generics in version 1.18. I just downloaded the latest beta version to test this major new feature.
Consider the code below:
package main
import "fmt"
func Demo1(n int) int {
return n
}
func Demo2(n int) {
fmt.Println(n)
}
func Call[T1, T2 any](fn func(T1) T2, param T1) {
fn(param)
}
func main() {
// Okay
Call(Demo1, 1)
// type func(n int) of Demo2 does not match
// inferred type func(int) T2 for func(T1) T2
Call(Demo2, 2)
}
The function Call accepts a function fn as a parameter, and calls it with the parameter param. The first call to Call is fine, the inferred type is int for both T1 and T2. However, the second call failed to compile.
I know I can always write an adapter to wrap Demo2:
wrapped := func(n int) int {
Demo2(n)
return -1
}
Call(wrapped, 2)
But that hurts performance and defeat the purpose of my current project.
Do you have any ideas to solve the problem? Or shall I fire a bug report?
Thanks!
Edit 1 (Background):
I am updating a benchmarking library I wrote 6 years ago to use generics. It targets to provide carefully written benchmark functions so that the benchmark results are more consistent than writing custom benchmarks independently.
For example, if a user wants to benchmark his function Sum of signature func(int) int, with the parameter values between 0 and 10, he can write this:
func BenchmarkSum(b *testing.B) {
butils.BenchmarkFnIntRetInt(b, Sum, butils.UniformDistribution(0, 10))
}
BenchmarkFnIntRetInt could be used to benchmark other func(int) int functions. The benchmark results would be more consistent than writing a separate routine, say BenchmarkProduct to benchmark the Product function. Since the grounds are the same, the benchmark results would be more consistent when comparing implementations by different authors.
In my library, the function signatures look like this:
func UniformDistribution(min, max int) func() int {
// ...
}
func BenchmarkFnIntRetInt(
b *testing.B,
target func(int) int,
paramGen func() int,
) {
// ...
}
Since Go has generics now, I could get rid of most of the redundant combinations (a.k.a. overloads, like one for func(int) int, and another for func(int) string, and yet another for func(int) bool), and replace them by generics instead. I hope the users can get rid of the overloads and just write:
func BenchmarkSum(b *testing.B) {
butils.BenchmarkGeneric(b, Sum, butils.UniformDistribution(0, 10))
}
The problem of icza's solution of writing one version for "non-void", and another for "void" is that there are a lot of higher order functions in my library. The number of versions I need to write grows exponentially as the number of function parameters grows.
For example, if I have fn1 and fn2, then 4 versions are needed; If I have fn1, fn2 and fn3, then 8 versions are needed; and so on.