本主题介绍 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# 语言规范中更详细地描述了类型推理。