共用方式為


自動產生 (F#)

更新:2010 年 5 月

F# 會使用型別推斷來評估函式和運算式的型別。 本主題描述 F# 如何自動一般化函式的引數和型別,讓函式在可能的情況下與多個型別搭配運作。

自動一般化

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

在下列程式碼範例中,將示範編譯器推斷為泛型的函式。

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

型別經推斷為 'a -> 'a -> 'a。

此型別表示這個函式接受兩個有相同未知型別的引數,並傳回相同型別的值。 先前函式可以是泛型的原因之一是大於運算子 (>) 本身為泛型。 大於運算子有簽章 'a -> 'a -> bool。 並非所有運算子都是泛型,而且如果函式中的程式碼一起使用參數型別與非泛型函式或運算子,該參數型別便無法一般化。

因為 max 是泛型,所以它可以搭配 int、float 等型別使用,如下列範例所示。

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"

值限制

只有當函式定義是具有明確引數的完整函式定義,而且值也是簡單不可變的值時,編譯器才會執行自動一般化。

這表示如果嘗試編譯的程式碼未充分限制為特定型別時,編譯器會發出錯誤,而且也不可一般化。 此問題的錯誤訊息將這個值的自動一般化限制稱為「值限制」(Value Restriction)。

當您要建構為泛型但編譯器沒有足夠資訊來一般化時,或當您無意中省略非泛型建構中的必要型別資訊時,通常會發生值限制錯誤。 若要解決值限制錯誤,可以透過下列其中一種方式,提供更明確的資訊以更完整地限制型別推斷問題:

  • 透過將明確型別附註加入至值或參數,將型別限制為非泛型。

  • 如果問題是使用不可一般化的建構定義泛型函式,例如函式複合或不完全套用的局部調用函式引數,請嘗試將函式重寫為一般函式定義。

  • 如果問題是運算式太複雜而無法一般化,請額外加入未使用的參數,讓它成為函式。

  • 加入明確泛型型別參數。 這個選項並不常使用。

  • 下列程式碼範例將示範上述每個案例。

案例 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 emptyset = Set.empty
// Adding a type parameter and type annotation lets you write a generic value.
let emptyset<'a> : Set<'a> = Set.empty

請參閱

參考

型別推斷 (F#)

泛型 (F#)

以統計方式解析的型別參數 (F#)

條件約束 (F#)

變更記錄

日期

記錄

原因

2010 年 5 月

已修正案例 2 中的程式碼。

內容 Bug 修正。