虽然,在 F# 中可以用尖括号指定类型参数值,与 C# 中的方式相同,但这种方法很少使用。原因是,当编译器无法推断出所有的信息,需要程序员的帮助时,我们仅在真正需要的地方,添加类型批注。我们用一个示例来演示:
> Option.map (fun dt -> dt.Year)(Some(DateTime.Now));;
error FS0072: Lookup on object ofindeterminate 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# 编译器对写高阶函数的帮助。