不同顺序的类方法中的类型推理的F#问题



此F#代码编译时没有出现错误

type A() = 
member private this.M1(tt: Task<'t>) = task {
let! t = tt
return t
}
member this.M4() = this.M2()
member this.M2() = this.M1(Task.FromResult(1))
member this.M3() = this.M1(Task.FromResult(1.0))

M4调用之后声明的M2。现在我移动最后的私有方法:

type A() = 
member this.M4() = this.M2()
member this.M2() = this.M1(Task.FromResult(1)) //FS0064
member this.M3() = this.M1(Task.FromResult(1.0)) //FS0001
member private this.M1(tt: Task<'t>) = task { //FS0064
let! t = tt
return t
}

此代码编译时没有出现以下错误:

警告FS0064此构造导致代码的泛型低于类型>注释。类型变量"t"已被约束为类型"int"。

错误FS0001此表达式应具有类型"int",但此处具有类型"float">

请解释发生这种情况的原因以及如何修复(如果可能(

F#编译器是单次通过的,包括它的类型推断。它根据程序从头到尾的使用情况来确定类型,并且不会重复(rec绑定或模块除外(。

在您的特定示例中,当编译器遇到方法M2时,它在其主体中看到方法M1是用类型为Task<int>的参数调用的,因此它推断M1 : Task<int> -> 'a对于一些未知的'a

当谈到方法M3的主体时,它看到M1是用类型为Task<float>的参数调用的,而这与它已经确定的类型信息不匹配,因此它发出了一个错误。

当它稍后谈到M1本身的定义时,它看到参数被声明为泛型。但是编译器已经知道参数必须是Task<int>,所以泛型参数't必须总是等于int,这就是它告诉你的:">类型变量"t"已被约束为类型"int">"。它发出了一个警告,因为很明显,如果你想让它是通用的,但它实际上是具体的,那么肯定有什么地方出了问题。

这种单向行为并不是一种缺陷或疏忽,它在很大程度上是故意的。这使得编译器更简单、更快、更高效,而且作为额外的好处,它通常会强制实现良好的程序结构。

最新更新