How do contravariant types work in golang 1.18 with generics?

huangapple go评论85阅读模式
英文:

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

  1. 不。
  2. 不是一个 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))
	}
}
英文:
  1. No.
  2. 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))
	}
}

huangapple
  • 本文由 发表于 2022年5月2日 20:46:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/72086935.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定