8

以下 F# 代码

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

不接受以下错误:

错误 FS0670 此代码不够通用。^T : (member get_Id : ^T -> int) 时的类型变量 ^T 无法泛化,因为它会超出其范围。

怎么了?如何解决?

编辑

@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 在 <^T 中给出错误

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

f2 在 ('T

错误 FS0583 不匹配 '('

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

接受 f3 和 f4

4

2 回答 2

11

你犯了三个错误:

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

其次,泛型参数名称需要以^代替前缀'。这实际上是可选的,因为编译器似乎会自动替换'^(从您的错误消息中可以明显看出),但这只是为了保持一致性。以 为前缀的泛型参数^称为“静态解析类型参数”,指的是它们在编译时被解析(和擦除)的事实,如上所述。

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

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

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 )

这是有关此的更多信息

于 2018-02-20T06:06:26.257 回答
4

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

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

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

于 2018-02-20T12:16:11.243 回答