泛型约束中的 F# 错误

Posted

技术标签:

【中文标题】泛型约束中的 F# 错误【英文标题】:F# error in generic constraint 【发布时间】:2018-02-20 03:45:42 【问题描述】:

以下 F# 代码

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

不被接受,并出现以下错误:

错误 FS0670 此代码不够通用。类型变量 ^T 当 ^T : (member get_Id : ^T -> 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 在 中给出错误

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

f2 在 ('T

错误 FS0583 不匹配 '('

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

f3 和 f4 被接受

【问题讨论】:

你想要SRTP 【参考方案1】:

你犯了三个错误:

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

Here's a bit more info on this.

【讨论】:

感谢您解释语法!你知道那个奇怪的调用语法记录在哪里吗?我检查了 f# 规范,但找不到指定在调用期间将参数作为元组传递的位置。这似乎是常识,但没有在任何地方指定。你在哪里学的这个语法? 在 F# 规范 4.1(撰写本文时的最新版本)中,这些被称为“成员约束”,并在第 5.2.3 节(规范)和 6.4.8(调用)中进行了描述。【参考方案2】:

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

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

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

【讨论】:

以上是关于泛型约束中的 F# 错误的主要内容,如果未能解决你的问题,请参考以下文章

在没有泛型类约束的情况下,将泛型类型与其默认值进行比较,会产生编译时错误

带有约束的打字稿泛型不能分配给泛型接口

C#泛型 类型约束

C# 泛型类型参数的约束

请教一个unity有关于泛型参数的问题

对 Func<T> 进行泛型类型约束