静态解析的类型参数
静态解析的类型参数是一种类型参数,它在编译时(而不是在运行时)被替换为实际类型。
语法
'type-parameter
直到 F# 7.0 版,都必须使用以下语法
^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)