[英]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.