可变类型 (F#)
“可变类型批注”指示参数、变量或值具有与所指定的类型兼容的类型,其兼容性是由面向对象的类或接口层次结构中的位置确定的。 可变类型非常有用,特别是,当未进行到类型层次结构中层次较高类型的自动转换,但您仍然希望您的功能能够使用层次结构中的任何或实现接口的任何类型时,可变类型尤为有用。
#type
备注
在上面的语法中,type 表示基类型或接口。
可变类型等效于具有约束的泛型类型,该约束将允许的类型限制为与基类型或接口类型兼容的类型。 也就是说,下面两行代码是等效的。
#SomeType
'a when 'a :> SomeType
可变类型在多种情况下非常有用。 例如,当您有高阶函数(将函数作为参数的函数)时,让函数返回可变类型通常非常有用。 在下面的示例中,通过将可变类型与 iterate2 中的序列参数一起使用,使得高阶函数能够使用生成序列、数组、列表以及任何其他可枚举类型的函数。
假设有以下两个函数,其中一个函数返回序列,另一个函数返回可变类型。
let iterate1 (f : unit -> seq<int>) =
for e in f() do printfn "%d" e
let iterate2 (f : unit -> #seq<int>) =
for e in f() do printfn "%d" e
// Passing a function that takes a list requires a cast.
iterate1 (fun () -> [1] :> seq<int>)
// Passing a function that takes a list to the version that specifies a
// flexible type as the return value is OK as is.
iterate2 (fun () -> [1])
另举一例,假设有以下 Seq.concat 库函数:
val concat: sequences:seq<#seq<'T>> -> seq<'T>
您可以将以下任何可枚举序列传递到此函数:
列表的列表
数组列表
列表的数组
序列的数组
任何其他可枚举序列的组合
下面的代码使用 Seq.concat 来演示您可通过使用可变类型支持的方案。
let list1 = [1;2;3]
let list2 = [4;5;6]
let list3 = [7;8;9]
let concat1 = Seq.concat [ list1; list2; list3]
printfn "%A" concat1
let array1 = [|1;2;3|]
let array2 = [|4;5;6|]
let array3 = [|7;8;9|]
let concat2 = Seq.concat [ array1; array2; array3 ]
printfn "%A" concat2
let concat3 = Seq.concat [| list1; list2; list3 |]
printfn "%A" concat3
let concat4 = Seq.concat [| array1; array2; array3 |]
printfn "%A" concat4
let seq1 = { 1 .. 3 }
let seq2 = { 4 .. 6 }
let seq3 = { 7 .. 9 }
let concat5 = Seq.concat [| seq1; seq2; seq3 |]
printfn "%A" concat5
输出如下所示。
seq [1; 2; 3; 4; ...]
seq [1; 2; 3; 4; ...]
seq [1; 2; 3; 4; ...]
seq [1; 2; 3; 4; ...]
seq [1; 2; 3; 4; ...]
在 F# 中,就像在其他面向对象的语言中一样,也有会将派生类型或实现接口的类型自动转换为基类型或接口类型的上下文。 这些自动转换通常在直接参数中进行,但是,当类型处于从属位置,作为更复杂的类型(例如函数类型的返回类型)的一部分或作为类型参数时,将不会进行这些自动转换。 因此,可变类型批注主要在您应用可变类型批注的类型是更复杂类型的一部分时有用。