在这一章,我们已经实现了几个 F# 的高阶函数,也看到了在 F# 和 C# 中并排的实现。F# 实现的很重要方面,是我们根本不需要指定类型;这是由于有了自动泛型化(automatic generalization),它用在推断函数声明的类型。我们将用Option.bind 函数的实现作为示例,介绍这个过程是如何工作方法的:
let bind func value = [1]
match value with [2]
|None –> None [3]
|Some(a) -> func(a) [4]
1、我们将一步一步地描述这个函数的类型推断过程。从最通用的可能类型开始,在处理代码中增加约束,这样,可以在清单中看到处理函数体时的过程。
2、使用声明签名[1]推断出 bind 是有两个参数的函数,为每个参数值和返回类型分配新的类型参数:
func : ‘t1
value : ‘t2
bind : ‘t1 -> ‘t2 -> ‘t3
3、使用模式匹配[2]推断出 value 是选项类型,因为,它是依据Some 和 None 模式进行匹配的。使用[3]推断出bind 的结果也是选项类型,因为,它可能有 None 值:
func : ‘t1
value : option<‘t4>
bind : ‘t1 -> option<‘t4> ->option<‘t5>
4、使用[4]推断出 func 是函数,因为我们将用一个参数去调用:
func : (‘t6 -> ‘t7)
value : option<‘t4>
bind : (‘t6 -> ‘t7) ->option<‘t4> -> option<‘t5>
5、 从[4]我们知道,函数的参数类型‘t4,与 bind 函数结果的类型相同,这样,我们可以添加下面两个约束:
‘t6 = ‘t4
‘t7 = option<‘t5>
6、现在,我们可使用在上一步得到的约束,来替换类型 ‘t6 和 ‘t7 :
func : (‘t4 -> option<‘t5>)
value : option<‘t4>
bind : (‘t4 -> option<‘t5>) ->option<‘t4> -> option<‘t5>
7、我们用 F# 的通常标准重新命名类型参数:
bind : (‘a -> option<‘b>) ->option<‘a> -> option<‘b>
虽然,使用这种描述实现 F# 类型推断算法是困难的,但它能够在推断高阶函数的类型时,了F# 可以使用何种信息。在这个过程中最有重要的步骤,可能是推断用作参数的函数(func)类型;这个步骤之所以重要,是因为作为参数的函数表示能够在在值上进行的操作。正如我们早些时候所见的,在某种意义上,这类似于方法,但由于有了类型推断,在 F# 中写这样的代码,不需要任何额外的类型说明,而且代码还是完全类型安全的。
有关类型推断与自动泛型化的简短插曲之后,我们还要回到如何编写和使用高阶函数上来。在第五章,我们已经讨论了大部分类型,但仍缺少一个重要的函数式值类型;在下一节,我们将通过高阶函数处理列表,解决更熟悉的领域的问题。