以統計方式解析的型別參數
F# 靜態已解析型別參數是一種型別參數,會在編譯時間為實際型別所取代,而不是執行階段。
語法
'type-parameter
7.0 版本之前的 F# 必須使用下列語法
^type-parameter
備註
在 F# 中,有兩種不同的型別參數。 第一種是標準泛型型別參數。 它們相當於其他 .NET 語言中的泛型型別參數。 另一種為靜態已解析型別參數,只能用於內嵌函式中。
靜態已解析型別參數主要用於與成員限制結合,這些限制可供您指定必須具有特定成員或多位成員才能使用的型別引數。 如果使用一般泛型型別參數,會無法建立這種限制。
下列表格摘要說明兩種型別參數之間的相似之處和差異。
功能 | 泛型 | 靜態已解析 |
---|---|---|
解析時間 | 執行時間 | 編譯時間 |
成員限制 | 無法搭配成員限制使用。 | 可搭配成員限制使用。 |
程式碼產生 | 具有標準泛型型別參數的型別 (或方法) 會導致產生單一泛型型別或方法。 | 會產生多個型別和方法的具現化,每個型別對應一個所需的具現化。 |
搭配型別使用 | 可用於型別。 | 不可用於型別。 |
搭配內嵌函式使用 | 內嵌函式無法以標準泛型型別參數進行參數化。 如果輸入不是完全泛型,F# 編譯器會將其特殊化,如果沒有特殊化選項,則會產生錯誤。 | 靜態已解析的型別參數無法用於非內嵌的函式或方法。 |
許多 F# 核心程式庫函式 (尤其是運算子) 都具有靜態已解析的型別參數,。 這些函式和運算子為內嵌,而且可為數值計算產生有效的程式碼。
使用運算子或具有靜態已解析型別參數的其他函式之內嵌方法和函式,本身也可使用靜態已解析的型別參數。 型別推斷通常會推斷這類的內嵌函式,以獲得靜態已解析的型別參數。 下列範例說明經推斷為具有靜態已解析型別參數的運算子定義。
let inline (+@) x y = x + x * y
// Call that uses int.
printfn "%d" (1 +@ 1)
// Call that uses float.
printfn "%f" (1.0 +@ 0.5)
(+@)
的已解析型別以 (+)
和 (*)
的使用為基礎,這兩者都會導致型別推斷在靜態已解析型別參數上的成員限制。 已解析的型別如下,就如 F# 解譯器所示。
'a -> 'c -> 'd
when ('a or 'b) : (static member ( + ) : 'a * 'b -> 'd) and
('a or 'c) : (static member ( * ) : 'a * 'c -> 'b)
輸出如下。
2
1.500000
下列範例說明如何搭配方法和靜態方法使用 SRTP:
type Record =
{ Number: int }
member this.Double() = { Number = this.Number * 2 }
static member Zero() = { Number = 0 }
let inline double<'a when 'a:(member Double: unit -> 'a)> (x: 'a) = x.Double()
let inline zero<'a when 'a:(static member Zero: unit -> 'a)> () = 'a.Zero()
let r: Record = zero ()
let doubleR = double r
從 F# 7.0 開始,您可使用 'a.Zero()
,而不需要如下列範例那樣重複限制。
從 F# 4.1 開始,您也可在靜態已解析型別參數的簽章中指定具體型別名稱。 在舊版的語言中,型別名稱由編譯器推斷,但無法在簽章中指定。 從 F# 4.1 開始,您也可在靜態已解析型別參數的簽章中指定具體型別名稱。 範例如下 (請注意,在此範例中,仍必須使用 ^
,因為系統不支援簡化使用 '
):
let inline konst x _ = x
type CFunctor() =
static member inline fmap (f: ^a -> ^b, a: ^a list) = List.map f a
static member inline fmap (f: ^a -> ^b, a: ^a option) =
match a with
| None -> None
| Some x -> Some (f x)
// default implementation of replace
static member inline replace< ^a, ^b, ^c, ^d, ^e when ^a :> CFunctor and (^a or ^d): (static member fmap: (^b -> ^c) * ^d -> ^e) > (a, f) =
((^a or ^d) : (static member fmap : (^b -> ^c) * ^d -> ^e) (konst a, f))
// call overridden replace if present
static member inline replace< ^a, ^b, ^c when ^b: (static member replace: ^a * ^b -> ^c)>(a: ^a, f: ^b) =
(^b : (static member replace: ^a * ^b -> ^c) (a, f))
let inline replace_instance< ^a, ^b, ^c, ^d when (^a or ^c): (static member replace: ^b * ^c -> ^d)> (a: ^b, f: ^c) =
((^a or ^c): (static member replace: ^b * ^c -> ^d) (a, f))
// Note the concrete type 'CFunctor' specified in the signature
let inline replace (a: ^a) (f: ^b): ^a0 when (CFunctor or ^b): (static member replace: ^a * ^b -> ^a0) =
replace_instance<CFunctor, _, _, _> (a, f)