类型推理 (F#)

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

类型推理概述

类型推理的理念是:除非编译器无法结论性地推断出类型,否则您不必指定 F# 构造的类型。 忽略显式类型信息并不表示 F# 是动态类型化语言或 F# 中的值为弱类型。 F# 是一种静态类型化语言,这意味着编译器会在编译过程中推断出每个构造的准确类型。 如果编译器没有足够的信息来推断出每个构造的类型,则您必须提供附加类型信息,通常通过在代码中的某处添加显式类型批注来进行。

参数和返回类型的推理

在参数列表中,您不必指定每个参数的类型。 但是,F# 是一种静态类型化语言,因此每个值和表达式在编译时都有明确的类型。 对于您未显式指定的那些类型,编译器将基于上下文推断类型。 如果未采用其他方式指定类型,则将类型推断为泛型。 如果代码使用值的方式不一致,而这样导致没有一种推断出的类型可满足值的所有用法,则编译器会报告错误。

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

例如,在下面的代码中,系统将参数类型 a 和 b 以及返回类型均推断为 int,原因是文本 100 的类型为 int。

let f a b = a + b + 100

可以通过更改文本来控制类型推理。 如果通过追加后缀 u 将 100 设置为 uint32,则系统会将 a、b 的类型以及返回类型推断为 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# 语言规范中对类型推理进行了更详细的介绍。

请参见

其他资源

自动泛化 (F#)