6.6.1 F# 中函数调用的类型推断
虽然,在 F# 中,使用尖括号指定类型参数值,是可能的,与在 C# 中的方式相同,但这种方法很少使用。原因是,当编译器无法推断出所有的信息,需要从程序员的一些帮助时,我们可以将类型批注添加到特定的,需要更多的信息的位置。让我们用一个示例来演示:
> Option.map (fun dt -> dt.Year) (Some(DateTime.Now));;
error FS0072: Lookup on object of indeterminate type.
> Option.map (fun (dt:DateTime) -> dt.Year) (Some(DateTime.Now));;
val it : int option = Some(2008)
不像在 C# 中,F# 中的参数值的顺序是重要的,因此,第一种情况会失败。原因是,F# 编译器要遇到第二个参数值时,才知道 dt 值的是 DateTime 类型,所以,它在处理第一个参数值时,不知道 Year 属性是否存在。要纠正此问题,我们在第二种情况中,添加了类型批注,显式指定 dt 值的类型。这是一个更有趣方面,使用流运算符:如果我们使用流来写前面的代码段,就不需要类型批注:
> Some(DateTime.Now) |> Option.map (fun dt -> dt.Year);;
val it : int option = Some(2008)
现在能工作,是因为,这个选项值,包含了 DateTime 值,出现得早,因此,它在 lambda 函数之前被处理。当处理 lambda 函数时,编译器已经知道 dt 的类型必须是 DateTime,这样,它就可以找到 year 属性,而不会有问题。
到目前为止,我们已经研究了 C# 和 F# 之间的相似之处,但 F# 中的类型推断更进一步。让我们看看 F# 编译器如何帮助能够我们写高阶函数。