类型推断
此主题介绍 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# 语言规范中对类型推理进行了更深入的介绍。