5.4.4 写泛型函数
大多数使用泛型类型的函数或方法都是高阶的,意味着它们取另一个函数作为参数值。这是一个重要的主题,我们会专门用一整章 (第 6 章)来讨论,但我们已经可以写一个泛函数,而不用误入高阶领域。我们将创建一个函数,它取一个选项类型,并返回包含的值。如果这个选项类型不包含一个值,该函数将引发异常。我们可以先看一下 C# 版本:
T ReadValue(Option opt) {
T v;
if (opt.MatchSome(out v)) return v;
else throw new InvalidOperationException();
}
如你所见,我们已经创建了具有单个类型参数的泛型方法。用在方法签名中的类型参数既作为一个返回值,也作为给泛型类型 Option 的参数。在方法体内,我们使用它再次声明这个类型的本地变量。总体而言,我们不得不 4 次提及 T。
这正是 F# 的类型推断确实非常出色的情况。看一下在 F# 中实现同样的事情。有趣的是,我们仍然不需要指定任何类型:
> let readValue(opt) =
match opt with
| Some(v) –> v
| None -> failwith "No value!";;
val readValue : 'a option –> 'a
可以看到从推断出的类型签名,函数正是泛型,与 C# 版本完全一样。允许这样的功能被称为自动泛型化(automatic generalization),我们后面会深入讨论到它,就目前而言,这里作一个 20 秒的说明:F# 类型推理算法搜索最通常的方式分配类型和其他一切作为泛型类型参数。这里,它知道参数值(opt)是选项类型,因为,我们根据 Some 和 None 识别器来匹配它。它也知道该函数返回包含在选项类型中的值,但它不知道是哪种类型,因此,它把这个类型看作泛型类型参数。
希望这已激起了你的兴趣,你期待聆讯更多关于自动泛型化和高阶函数的内容——但是,首先让我们完成常见的函数值之旅。在其他语言中,通常不把函数看作值,但这是使函数编程如此强大而优雅的一个重要方面。