Contravariant Types in Go 1.18 Generics
In Go 1.18, the introduction of generics has sparked interest in contravariant types. A key question that arises is how they function within the context of generics.
Unfavorable Behavior: Incompatible Types
Consider the following code snippet:
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)) } }
When attempting to use Pipe with the following functions:
func OpenFile(name string) *os.File { ... } func ReadAll(rdr io.Reader) []byte { ... }
The compilation fails because the compiler treats T1 as *os.File, which is not identical to io.Reader.
The Root Cause: Contravariant Semantics
The issue stems from the nature of contravariant types. In this case, T1 is expected to be a more specific type than A, meaning functions that accept T1 can also accept A. However, Go generics do not support covariant result types. Thus, Functions that return T1 cannot return A, even if they are implicitly convertible.
Resolution and Consequences
There is currently no way to modify the signature of Pipe in Go 1.18 to allow for the desired behavior. This is not considered a bug but rather an intentional design choice.
Workaround: Type Conversion
To circumvent this limitation, one can resort to type conversion at runtime:
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, this approach sacrifices compile-time type safety.
The above is the detailed content of How Do Contravariant Types Work in Go 1.18 Generics?. For more information, please follow other related articles on the PHP Chinese website!