6.4.4 实现选项类型的操作
绑定(bind)和映射(map)的实现有类似的结构,因为,两者都是依据选项值进行模式匹配的高阶函数。我们来看一看 F# 和 C# 的实现,这是在 C# 中实现函数式概念的最好示例。我们先看一下清单 6.14,这是映射操作的实现。
清单 6.14 用 F# 和 C# 实现 map 操作
F# Interactive | C# |
> let map f input = match input with | None –> None | Some(value) –> Some(f(value));; val map : ('a -> 'b) –> 'a option -> 'b option | Option<R> Map<T, R>(this Option<T> input, Func<T, R> f) { T v; if (input.MatchSome(out v)) return Option.Some(f(v)); else return Option.None<R>(); } |
程序首先检查作为参数值给定的选项值,当值为 None,立即返回结果 None。注意,我们不能返回作为参数值的 None 值,因为类型可能不同;在 C# 版本中,更为明显;结果的类型是 Option<R>,而参数的类型是 Option<T>。
当参数值匹配差别联合 Some 分支的值,得到的类型T 的值,使用提供的函数(或 Func 委托),把它映射到类型R 的值。从操作返回值的类型应该是 Option<R>,需要再次使用 Some 构造函数把这个值打包。
尽管映射和绑定操作的源代码非常相似,但也有一些重要的差异。现在我们来看一下在清单 6.15 中的第二组操作。
清单 6.15 用 F# 和 C# 实现bind 操作
F# Interactive | C# |
> let bind f input = match opt with | None –> None | Some(value) -> f(value) ;; val bind : ('a -> 'b option) -> 'a option -> 'b option | Option<R> Bind<T, R>(this Option<T> input, Func<T, Option<R>> f) { T value; if (input.MatchSome(out value)) return f(value); else return Option.None<R>(); } |
绑定操作同样从对作为参数值给定的选项值进行模式匹配开始,当选项值是 None 时,如同前面的情况一样,立即返回 None;差别在于选项包含实际值的情况。我们再次应用从参数值得到的函数,但这一次,不需要把结果打包到Some构造函数中;因为从函数返回的值已经是选项类型了,从类型签名可以看出,这也正是我们要返回的类型。因此,即使在 Some 分支,绑定操作仍可能返回 None,具体取决于用户所提供的函数。
通常,F# 版本把原始值作为最后一个参数值,这样可以利用管道和散应用,而 C# 版本采用扩展方法。现在,我们看一下如何在 C# 中,使用新创建的方法,重写前面的示例。