此 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))
Run Code Online (Sandbox Code Playgroud)
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
}
Run Code Online (Sandbox Code Playgroud)
此代码无法编译并出现以下错误:
警告 FS0064 此构造导致代码不如类型>注释所指示的通用。类型变量“t”已被限制为“int”类型。
错误 FS0001 该表达式的类型应为“int”,但此处的类型为“float”
请解释为什么会发生这种情况以及如何解决它(如果可能)
F# 编译器是单遍编译器,包括其类型推断。它根据程序从头到尾的使用来确定类型,并且不会重复(rec绑定或模块除外)。
在您的特定示例中,当编译器遇到 method 时M2,它在其主体中看到该 methodM1正在使用 type 的参数进行调用Task<int>,因此它推断M1 : Task<int> -> 'a对于某些未知的'a
当涉及到 method 的主体时M3,它发现M1正在使用 type 的参数进行调用Task<float>,并且这与它已经确定的类型信息不匹配,因此它会发出错误。
当稍后涉及M1其自身的定义时,它看到该参数被声明为泛型。但编译器已经知道参数必须是Task<int>,因此泛型参数't必须始终等于int,这就是它告诉您的内容:“类型变量 't 已被约束为类型 'int' ”。它发出了一个警告,因为很明显,如果你希望它是通用的,但它实际上是具体的,那么一定有什么地方出了问题。
这种单向行为并不是缺陷或疏忽,而是有意为之。这使得编译器变得更简单、更快、更高效,而且作为奖励,它通常会强制要求良好的程序结构。