5.4.4 实现选项类型的操作
绑定和映射的实现有类似的结构,因为,两者都是高阶函数,模式匹配依据一个选项值。我们来看一看 F# 和 C# 的实现,是在 C# 中编码函数概念的好示例。让我们从清单 6.14 开始,显示了映射操作的实现。
Listing 6.14 Implementing the map operation in F# and C#
F# Interactive | C# |
> let map f input = | Option Map(this Option |
实现首先检查给定的选项值作为参数值。当值为 None,它立即返回 None 作为结果。注意,我们不能返回作为参数值的 None 值,因为类型可能不同。在 C# 版本中,更为明显。结果的类型是 Option,而参数值的类型是 Option。
当参数值的值,匹配差别【送别】联合的 Some 情况,就得到 T 类型的值,并使用提供的函数(或 Func 委托),把它映射到 R 类型的值。因为,从这个操作返回的值的类型应该是 Option,需要再次使用 Some 构造函数把这个值打包。
映射和绑定操作的源代码是很相似,但也有一些重要的差异。现在让我们看一下,在清单 6.15 中的第二组操作。
Listing 6.15 Implementing the bind operation in F# and C#
F# Interactive | C# |
> let bind f input = | Option Bind(this Option |
绑定操作同样从模式匹配开始,依据对作为参数值给定的选项值。当选项值是 None 时,它立即返回 None,就像前面的情况一样。区别在于,当选项携带实际值。我们再次应用来自参数值的函数,但这一次,不需要把结果打包到Some构造函数中 。从函数中返回的值已经是一个选项,并且可以从类型签名中看出,这也正是我们想要返回的类型。这意味着,即使在 Some 情况下,绑定操作仍可能返回 None,具体情况要取决于用户所提供的函数。
通常,F# 版本把原始值作为最后一个参数值,以启用流及偏应用,而 C# 版本采用扩展方法。现在,我们看一下如何在 C# 中重写前面的示例,使用新创建的方法。
在 C#中使用选项类型
扩展方法使我们可以用流畅的风格来编写使用绑定和映射的代码。因为,括号中的数字可能会造成混乱,因此,要注意对映射的调用是嵌套在 lambda 函数内部的,作为参数值给绑定的:
Option ReadAndAdd() {
return ReadInput().Bind(n =>
ReadInput().Map(m => m + n));
}
在 C# 中,使用高阶函数与显式处理选项类型之间的区别更为重要。C# 不直接支持类似差别联合之类的类型,但如果我们提供的类型有适当的处理函数,代码将变得可读。这是重要的一点,牢记,在 C# 中写函数式程序:一些低级的构造可能感觉不自然的,但由于 lambda 函数,我们在 C# 中也可以编写优雅的函数代码。
到目前为止,我们已经看到了如何使用高阶函数处理多值和可选值。在前一章中,我们谈到了最后一种值是函数。在下一节,我们将看到,也可以写非常有用的高阶函数值来处理函数值。