自動一般化

F# 會使用類型推斷來評估函式和運算式的類型。 本主題描述 F# 如何自動將引數和函式類型泛化,以便盡可能使用多個類型。

自動一般化

F# 編譯器會在函式上執行類型推斷時,判斷指定的參數是否可以是泛型。 該編譯器會檢查每個參數,並判斷函式是否相依於該參數的特定類型。 如果沒有,則會推斷該類型為泛型。

下列程式碼範例說明編譯器推斷為泛型的函式。

let max a b = if a > b then a else b

該類型會推斷為 'a -> 'a -> 'a

該類型指出這是一個接受相同未知類型的兩個引數並傳回該相同類型的值的函式。 前面的函式可以是泛型的其中一個原因是大於運算子 (>) 本身就是泛型。 大於運算子具有簽章 'a -> 'a -> bool。 並非所有運算子都是泛型,而且如果函式中的程式碼搭配非泛型函式或運算子使用參數類型,則無法泛化該參數類型。

因為 max 是泛型,所以它可以與 intfloat 等類型一起使用,如下列範例所示。

let biggestFloat = max 2.0 3.0
let biggestInt = max 2 3

但是,這兩個參數都必須屬於同一類型。 簽章是 'a -> 'a -> 'a,而不是 'a -> 'b -> 'a。 因此,下列程式碼會產生錯誤,因為類型不相符。

// Error: type mismatch.
let biggestIntFloat = max 2.0 3

max 函式也適用於任何支援大於運算子的類型。 因此,您也可以在字串上使用它,如下列程式碼所示。

let testString = max "cab" "cat"

值限制

編譯器只會對具有明確引數的完整函式定義,以及簡單的不可變值執行自動泛化。

這表示,如果您嘗試編譯的程式碼未充分限制為特定類型,而且也無法進行泛化時,編譯器就會發出錯誤。 此問題的錯誤訊息會將這種對值自動泛化的限制稱為值限制

一般而言,當您想要建構成為泛型但編譯器沒有足夠的資訊來將它泛化時,或當您無意中在非泛型建構中省略了足夠的類型資訊時,就會發生值限制錯誤。 值限制錯誤的解決方案是,透過下列其中一種方式提供更明確的資訊,以更充分地限制類型推斷問題:

  • 藉由將明確類型註釋新增至值或參數,來將類型限制為非泛型。

  • 如果問題是使用不可泛化的建構來定義泛型函式 (例如函式組合或未完全套用的 curried 函式引數),請嘗試將函式重寫為一般函式定義。

  • 如果問題是太複雜而無法泛化的運算式,請藉由新增一個額外未使用的參數,將它變成函式。

  • 新增明確的泛型類型參數。 此選項很少使用。

下列程式碼範例說明以下每一個案例。

案例 1:使運算式複雜化。 在此範例中,清單 counter 的目的是要為 int option ref,但它未定義為一個簡單的不可變值。

let counter = ref None
// Adding a type annotation fixes the problem:
let counter : int option ref = ref None

案例 2:使用一個不可泛化的建構來定義泛型函式。 在此範例中,建構是不可泛化的,因為它牽涉到函式引數的部分應用程式。

let maxhash = max << hash
// The following is acceptable because the argument for maxhash is explicit:
let maxhash obj = (max << hash) obj

案例 3:新增一個額外未使用的參數。 因為此運算式對於泛化不夠簡單,所以編譯器會發出值限制的錯誤。

let emptyList10 = Array.create 10 []
// Adding an extra (unused) parameter makes it a function, which is generalizable.
let emptyList10 () = Array.create 10 []

案例 4:新增類型參數。

let arrayOf10Lists = Array.create 10 []
// Adding a type parameter and type annotation lets you write a generic value.
let arrayOf10Lists<'T> = Array.create 10 ([]:'T list)

在最後一種情況下,該值變成一個類型函式 (可用來建立許多不同類型的值),如下例如下:

let intLists = arrayOf10Lists<int>
let floatLists = arrayOf10Lists<float>

另請參閱