以下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被接受
答案 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 )
答案 1 :(得分:4)
要添加关于您添加的编辑的简短评论 - 您对f1
的定义的问题只是解析器需要尖括号和帽子之间的空格:<^
:< / p>
let inline f1< ^T when ^T: (member Id:int)> (t:^T) = ( ^T: (member Id:int) t )
否则,语法<^
将被解析为运算符,而不是通用参数列表,这就是您需要的。所有其他信息都在Fyodor的答案中!