英文:
How do contravariant types work in golang 1.18 with generics?
问题
在golang 1.18中,我想定义一个如下所示的函数:
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))
}
}
例如,left
函数的输出应作为 right
函数的输入,表示为泛型。
我注意到对于以下示例,这个函数无法按预期工作:
func OpenFile(name string) *os.File {
...
}
func ReadAll(rdr io.Reader) []byte {
...
}
var OpenRead = Pipe(OpenFile, ReadAll)
这个函数无法编译通过,因为编译器认为 T1
是 *os.File
类型,尽管它与 io.Reader
兼容,但并不完全相同。
如果我不使用泛型调用链,而是这样调用:
var result = ReadAll(OpenFile("test"))
那么编译器会识别出兼容的类型。
问题:
- 在 golang 1.18 的泛型中,有没有办法修复
Pipe
函数的签名以实现所需的行为? - golang 1.18 的行为是设计如此,还是这是一个 bug?
英文:
In golang 1.18 I would like to define a function like follows:
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))
}
}
e.g. the output of the left
function should be the input of the right
function, repesented as generics.
I am noticing that this fails to work as expected for the following example:
func OpenFile(name string) *os.File {
...
}
func ReadAll(rdr io.Reader) []byte {
...
}
var OpenRead = Pipe(OpenFile, ReadAll)
This fails to compile because the compiler considers T1
to be *os.File
and although it's compatible to io.Reader
it's not identical.
If I were to invoke the chain without templates like so:
var result = ReadAll(OpenFile("test"))
then the compiler identifies the compatible types.
Questions:
- is there a way in golang 1.18 generics to fix the signature of
Pipe
to allow for the desired behaviour? - is the golang 1.18 behaviour by design or is this a bug?
答案1
得分: 4
- 不。
- 不是一个 bug。请参阅常见问题解答。
鉴于 Go 不支持协变的结果类型,你需要将 left
的结果转换为 right
接受的类型。然而,目前无法使用类型参数来表示可转换性。
如果你愿意,你可以根据链接中的示例调整你的代码,你将得到类似这样的结果,但请记住,它不是“编译时类型安全”的。
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))
}
}
英文:
- No.
- No, not a bug. See the FAQ.
Given that Go does not support covariant result types you'd need to convert the result of the left
to the type accepted by the right
. However there's currently no way to express convertibility using type parameters.
If you want you could adapt your code based on the example in that link and you'll get something like this, but keep in mind that it's not "compile-time type-safe".
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))
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论