簡體   English   中英

F#編譯器如何/何時預讀類型推斷?

[英]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'bDictionary<'a,'b> ,但它不是通用定義,因為沒有通用值這樣的東西(好吧,差不多;參見[<GeneralizableValue>]使用[<GeneralizableValue>]例外)。

基於很久以前對規范的解讀,我的理解是編譯器實際上並沒有向前看,而是在它出現時創建類型變量,識別每個變量的約束,然后將實際類型分配給變量看完一切后。 所以在第二種情況下,當它讀取add定義時,它會為參數創建類型變量'a'b 然后它進入調用add 5.4 3.2 ,它將'a'b都限制為float

編譯時, let add xy = x + y ,沒有什么可以約束類型,所以編譯器選擇int

由於在讀取整個編譯單元后解決了類型約束,因此可以通過說“始終”來回答您的問題。 您正在質疑的行為只出現在F#interactive中,其中編譯單元較小。 編譯是以位而不是一次完成的。

add的情況下,編譯器將參數類型默認為int 如果您改為編寫let inline add xy = x + y ,編譯器將自動將xy的類型概括為實現+運算符的類型。 這是靜態解析的類型參數的示例。 當默認addint的參數類型時,F#將在當前范圍內查找。

str.Replace的情況下,方法Replace是string上的實例方法。 因此,編譯器必須知道str的類型以對其實例方法做出任何確定。 或者,在上面的示例中, +是一個靜態函數 - 編譯器知道前面涉及的類型和約束。 有關F#類型推斷的更多信息,請查看此處

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM