Fra*_*ron 7 f# generic-constraints
以下F#代码
let f<'T when 'T: (member Id:int)> (t:'T) = t.Id
不接受以下错误:
错误FS0670此代码不够通用.^ T :(成员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 )
Run Code Online (Sandbox Code Playgroud)
f1在<^ T中给出错误
错误FS0010模式中出现意外的中缀运算符
f2给出错误('T
错误FS0583无与伦比'('
错误FS0010绑定中出现意外的引号
f3和f4被接受
Fyo*_*kin 10
你有三个错误:
首先,功能需要inline..NET CLR目前不支持成员约束(即"只要它有这个成员就可以是任何类型"),这意味着这样的函数不能编译成IL,所以F#编译器必须伪造它并替换它们编译时的函数.要通知编译器您知道并同意,您必须在之后添加inline关键字let.内联函数将在编译时完全擦除,并且不会在编译代码中显示为CLR方法.
其次,通用参数名称需要加上前缀^而不是'.这实际上是可选的,因为编译器似乎是自动替换'用^(因为显而易见的错误消息),但只是为了一致性.前缀的通用参数^称为"静态解析类型参数",它指的是它们在编译时被解析(和擦除)的事实,如上所述.
第三,在函数体中引用这些成员的语法实际上与引用常规成员的语法不同.您不能使用点表示法.相反,您必须使用镜像参数声明的这种奇怪的语法.
应用所有三个修复程序,这将是您的新代码:
let inline f<^T when ^T: (member Id:int)> (t:^T) = ( ^T: (member Id:int) t )
Run Code Online (Sandbox Code Playgroud)
请注意,由于成员约束现在在函数体中,因此不必在左侧重复=,因此您可以这样写:
let inline f t = ( ^T: (member Id:int) t )
Run Code Online (Sandbox Code Playgroud)