类型推断

此主题介绍 F# 编译器如何推断值、变量、参数和返回值的类型。

一般类型推理

类型推理的理念是,无需指定 F# 构造的类型,除非编译器无法最终推断出类型。 省略显式类型信息并不意味着 F# 是动态类型化语言或 F# 中的值是弱类型。 F# 是一种静态类型化语言,这意味着编译器会在编译过程中为每个构造推导一个确切的类型。 如果编译器没有足够的信息来推导每个构造的类型,你需要提供额外的类型信息,方法通常是在代码中的某个位置添加显式类型批注。

参数和返回类型的推断

在参数列表中,无需指定每个参数的类型。 但 F# 是一种静态类型化的语言,因此,每个值和表达式在编译时都有一个明确的类型。 对于未显式指定的类型,编译器将根据上下文推断类型。 如果未指定某类型,则将其推断为泛型。 如果代码使用某个值的方式不一致,以至于不存在可推断出的满足该值所有用法的单个类型,编译器将报告错误。

函数的返回类型由函数中最后一个表达式的类型确定。

例如,在下面的代码中,参数类型 ab 和返回类型全都推断为 int,因为文本 100 的类型为 int

let f a b = a + b + 100

可以通过更改文本来影响类型推理。 如果通过附加后缀 u 使 100 成为 uint32,则 ab 的类型和返回值将推断为 uint32

还可以通过使用隐含类型限制的其他构造(如仅适用于特定类型的函数和方法)来影响类型推理。

此外,还可以将显式类型批注应用于函数或方法参数或表达式中的变量,如下面的示例中所示。 如果不同约束之间存在冲突,将导致错误。

// Type annotations on a parameter.
let addu1 (x : uint32) y =
    x + y

// Type annotations on an expression.
let addu2 x y =
    (x : uint32) + y

还可以通过在所有参数后面提供类型批注来显式指定函数的返回值。

let addu1 x y : uint32 =
   x + y

类型批注有益于参数的常见情况是:参数是对象类型,并且要使用成员。

let replace(str: string) =
    str.Replace("A", "a")

自动泛化

如果函数代码不依赖于参数的类型,则编译器会将参数视为泛型。 这称为自动泛化,这在编写泛型代码时会非常有用,并且不会增加复杂性。

例如,下面的函数将任意类型的两个参数合并为一个元组。

let makeTuple a b = (a, b)

推断类型为

'a -> 'b -> 'a * 'b

其他信息

F# 语言规范中对类型推理进行了更深入的介绍。

另请参阅