类型推理 (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# 语言规范中对类型推理进行了更详细的介绍。