通用约束中的F#错误

时间:2018-02-20 03:45:43

标签: f# generic-constraints

以下F#代码

let f<'T when 'T: (member Id:int)> (t:'T) = t.Id

不接受以下错误:

  

错误FS0670此代码不够通用。类型变量   ^ T时^ T :(成员get_Id:^ T - &gt; int)无法推广   因为它会逃避它的范围。

怎么了? 如何解决?

修改

@Fyodor:很棘手!我做了一些测试,发现更多的陌生感:

let inline f1<^T when ^T: (member Id:int)> (t:^T) = ( ^T: (member Id:int) t )

let inline f2<'T when 'T: (member Id:int)> (t:'T) = ( 'T: (member Id:int) t )

let inline f3<'T when 'T: (member Id:int)> (t:'T) = ( ^T: (member Id:int) t )

let inline f4 t = ( ^T: (member Id:int) t )

f1在&lt; ^ T

中给出错误
  

错误FS0010模式中的意外中缀运算符

f2给出错误('T

  

错误FS0583不匹配'('

     

错误FS0010绑定中的意外引号符号

f3和f4被接受

2 个答案:

答案 0 :(得分:10)

你有三个错误:

首先,该功能必须为inline。 .NET CLR当前不支持成员约束(即&#34;只要它有这个成员就可以是任何类型&#34;),这意味着这样的函数不能编译成IL,所以F#编译器必须伪造并在编译时替换这些函数。要向编译器发出您知道并同意的信号,您必须在inline之后添加let关键字。内联函数将在编译时完全擦除,并且不会在编译代码中显示为CLR方法。

第二,通用参数名称需要以^为前缀,而不是'。这实际上是可选的,因为编译器似乎自动用'替换^(从错误消息中可以明显看出),但仅仅是为了保持一致性。前缀为^的通用参数称为&#34;静态解析类型参数&#34;,它指的是在编译时它们被解析(和擦除)的事实,如上所述。

第三,在函数体中引用此类成员的语法实际上与引用常规成员的语法不同。您不能使用点符号。相反,你必须使用这种反映参数声明的奇怪语法。

应用所有三个修复程序,这将是您的新代码:

let inline f<^T when ^T: (member Id:int)> (t:^T) = ( ^T: (member Id:int) t )

请注意,由于成员约束现在位于函数体中,因此不必在=的左侧重复,因此您可以这样写:

let inline f t = ( ^T: (member Id:int) t )

Here's a bit more info on this

答案 1 :(得分:4)

要添加关于您添加的编辑的简短评论 - 您对f1的定义的问题只是解析器需要尖括号和帽子之间的空格:<^:< / p>

let inline f1< ^T when ^T: (member Id:int)> (t:^T) = ( ^T: (member Id:int) t )

否则,语法<^将被解析为运算符,而不是通用参数列表,这就是您需要的。所有其他信息都在Fyodor的答案中!