Generics with Contravariant Types in Go 1.18: Clarification and Limitations
Efforts to define a function using generics in Go 1.18 that takes two functions with compatible, but not identical, types - a contravariant behavior - have met with challenges. Let's delve into the specifics to understand why.
Consider the following function definition:
func Pipe[A, T1, T2 any](left func(A) T1, right func(T1) T2) func(A) T2 { return func(a A) T2 { return right(left(a)) } }
This function aims to pipe the output of the left function into the right function, chaining computations. However, when attempting to use it with the following example:
func OpenFile(name string) *os.File { ... } func ReadAll(rdr io.Reader) []byte { ... } var OpenRead = Pipe(OpenFile, ReadAll)
compilation fails. This is because the compiler expects T1 to be identical to *os.File, even though it is compatible with io.Reader. While it may seem reasonable to expect compatible types to be accepted, this is not the case due to Go's lack of support for covariant result types.
Is there a way to fix this signature in Go 1.18?
Unfortunately, no. Go's generics currently lack the ability to express type convertibility using type parameters, making it impossible to modify the Pipe function to allow for this behavior.
Is this a bug in Go 1.18?
No. As stated in the official FAQ, this behavior is intentional and not considered a bug.
Workaround
To achieve a similar result, one can implement a conversion step manually:
func Pipe[A, T1, T2, T3 any](left func(A) T1, right func(T2) T3) func(A) T3 { return func(a A) T3 { return right(any(left(a)).(T2)) } }
However, it's important to note that this approach does not provide compile-time type safety.
The above is the detailed content of Can Go 1.18 Generics Handle Contravariant Types in Function Signatures?. For more information, please follow other related articles on the PHP Chinese website!