函数值带有近似约束导致实例化失败。

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

Instantiation failure caused by function value with approximate constraint

问题

对于上面的代码,如果我像这样实例化它,运行时会出错(无法推断出 S):

b := GAddAll[int]
fmt.Printf("%v", b(3, []int{1, 2}))

但是像这样使用它就可以正常工作:

fmt.Printf("%v", GAddAll[int](3, []int{1, 2}))

我想知道为什么。

英文:
func GAddAll[E int, S ~[]E](e E, s S) S {
   copyS := make(S, len(s))
   for i, v := range s {
   copyS[i] = v + e
   }
   return copyS
}

For the above code, if I instantiate it like this, it will give an error when running(cannot infer S)

b := GAddAll[int]
fmt.Printf("%v", b(3, []int{1, 2}))

But it works fine like this

fmt.Printf("%v", GAddAll[int](3, []int{1, 2}))

I want to know why.

答案1

得分: 2

实例化失败是因为S ~[]E具有近似约束,并且没有足够的类型信息来实例化S

当你使用以下代码给函数赋值时:

b := GAddAll[int]

函数已经被实例化。引用规范:

> 一个未被调用的泛型函数需要一个类型参数列表来进行实例化;如果列表是部分的,那么所有剩余的类型参数必须是可推断的

然而,S是不可推断的。编译器只能获得的信息是类型参数E。在第一次替换后,编译器只能推断出约束~[]E,现在是~[]int,但类型参数S仍然未知。

由于带有波浪符(~)的近似类型集是几乎无限的,无法确定S是什么——它可以是type FooSlice []int,因此实例化失败。

因此,如果你需要传递函数值,必须通过提供两个类型参数来进行实例化:

b := GAddAll[int, []int] // ok
fmt.Printf("%v", b(3, []int{1, 2}))

而对于调用表达式:

GAddAll[int](3, []int{1, 2})

编译器仍然无法从类型参数int推断出S,但它可以从非类型参数[]int{1,2}推断出S,然后实例化函数。

> 一个被调用的泛型函数可以提供(可能是部分的)类型参数列表,或者可以完全省略它,如果省略的类型参数可以从普通(非类型)函数参数中推断出来

作为澄清,如果你从S ~[]E中移除波浪符,像这样:

func GAddAll[E int, S []E](e E, s S) S {}

推断将成功,只需提供E,因为S的约束的类型集的基数将为1(一个确切的类型)。

英文:

Instantiation fails because S ~[]E has an approximate constraint, and there isn't enough type information to instantiate S.

When you assign the function value with:

b := GAddAll[int]

the function is already being instantiated. Quoting the spec:

> A generic function that is is not called requires a type argument list for instantiation; if the list is partial, all remaining type arguments must be inferrable.

However S isn't inferrable. The only information available to the compiler is the type parameter E. After a first pass of substitution, the compiler is only able to infer the constraint ~[]E, which is now ~[]int, but the type argument S is still unknown.

Given that approximate type sets with tilde (~) are virtually infinite, there's no way to conclusively determine what S is — it could be type FooSlice []int — and instantiation fails.

So if you need to pass around the function value, you must instantiate by supplying both type parameters:

b := GAddAll[int, []int] // ok
fmt.Printf("%v", b(3, []int{1, 2}))

Instead with the call expression:

GAddAll[int](3, []int{1, 2})

the compiler still can't infer S from the type argument int, but it can infer it from the non-type argument []int{1,2}, and then instantiate the function.

> A generic function that is called may provide a (possibly partial) type argument list, or may omit it entirely if the omitted type arguments are inferrable from the ordinary (non-type) function arguments.

As a clarification, if you were to remove the tilde from the S ~[]E, like this:

func GAddAll[E int, S []E](e E, s S) S {}

inference would succeed by supplying only E, because the type set of S's constraint would have cardinality 1 (an exact type).

huangapple
  • 本文由 发表于 2022年7月19日 10:34:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/73030503.html
匿名

发表评论

匿名网友

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

确定