[英]How/when does the F# compiler read ahead for type inference?
如果我在下面创建添加功能
let add x y =
x + y
然后单独运行下面的行,我会收到一个错误,因为F#默认假设x和y应该是整数。
add 5.4 3.2
也就是说,如果我一起运行它们,添加工作就好了,因为它现在将其视为一个获得两个浮点数的函数(这意味着编译器向前看进行类型推断)。
这引出了一个问题,为什么不同的向前看过程允许F#知道toHackerTalk正在接受一个字符串? 即使我一起运行以下行,它也会给我一个错误,因为短语的类型是不确定的。
let toHackerTalk phrase =
phrase.Replace('t', '7').Replace('o', '0')
toHackerTalk "this be strange"
我认为这是对此的简单看法。 在第一种情况下,编译器知道+
(它在F#核心中定义)并且它只是选择一个默认类型的int
(可能在某个地方的规范中定义),如果它然后看到float
使用,不同版本的+
被选中。
另一方面,编译器没有足够的信息来选择具有Replace
成员的类型,因此无法编译该函数
该规范在5.2.3节中提到了这种行为:
除非将定义标记为内联,否则重载运算符的使用不会产生通用代码。 例如,功能
let fx = x + x
导致函数f只能用于添加一种类型的值,例如int或float。 确切的类型由后来的约束决定。
一般来说,只要编译器推断出由于某种原因而无法真正通用的“通用”定义,就会出现这个问题。 在使用像(+)
之类的运算符的情况下,有一个静态成员约束,它阻止add
是泛型的,除非它也是inline
声明的。
另一种出现这种限制的情况是:
let d = System.Collections.Generic.Dictionary()
d.[0] <- "test"
如果你一起评估这些行, d
将是一个Dictionary<int,string>
,但如果你单独评估它们,你会在评估第二个时得到一个错误,因为d
将被推断为一个Dictionary<obj,obj>
。 根据它的定义, d
可以是任何'a
和'b
的Dictionary<'a,'b>
,但它不是通用定义,因为没有通用值这样的东西(好吧,差不多;参见[<GeneralizableValue>]
使用[<GeneralizableValue>]
例外)。
基于很久以前对规范的解读,我的理解是编译器实际上并没有向前看,而是在它出现时创建类型变量,识别每个变量的约束,然后将实际类型分配给变量看完一切后。 所以在第二种情况下,当它读取add定义时,它会为参数创建类型变量'a
和'b
。 然后它进入调用add 5.4 3.2
,它将'a
和'b
都限制为float
。
编译时, let add xy = x + y
,没有什么可以约束类型,所以编译器选择int
。
由于在读取整个编译单元后解决了类型约束,因此可以通过说“始终”来回答您的问题。 您正在质疑的行为只出现在F#interactive中,其中编译单元较小。 编译是以位而不是一次完成的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.