选件模块支持铁路定向编程

错误处理很重要,但可以使优雅的算法陷入混乱。铁路定向编程ROP)用于使错误处理更加优雅和可组合。

考虑一下简单的函数 f

let tryParse s =
  let b, v = System.Int32.TryParse s
  if b then Some v else None

let f (g : string option) : float option =
  match g with
  | None    -> None
  | Some s  ->
    match tryParse s with           // Parses string to int
    | None              -> None
    | Some v when v < 0 -> None     // Checks that int is greater than 0
    | Some v -> v |> float |> Some  // Maps int to float

f 的目的是将输入 string 值(如果有 Some)解析为 int。如果 int 大于 0,我们将它投入 float。在所有其他情况下,我们用 None 拯救。

虽然嵌套 match 的功能非常简单,但显着降低了可读性。

ROP 观察到我们的程序中有两种执行路径

  1. 快乐的道路 - 最终会计算出 Some 的价值
  2. 错误路径 - 所有其他路径生成 None

由于错误路径更频繁,因此它们倾向于接管代码。我们希望快乐路径代码是最明显的代码路径。

使用 ROP 的等效函数 g 可能如下所示:

let g (v : string option) : float option =
  v
  |> Option.bind    tryParse  // Parses string to int
  |> Option.filter  ((<) 0)   // Checks that int is greater than 0
  |> Option.map     float     // Maps int to float

它看起来很像我们倾向于处理 F# 中的列表和序列。

人们可以看到 Option<'T>List<'T> 只能包含 01 元素,其中 Option.bind 的行为类似于 List.pick(概念上 Option.bind 可以更好地映射到 List.collect 但是 List.pick 可能更容易理解)。

bindfiltermap 处理错误路径,g 只包含快乐路径代码。

所有直接接受 Option<_> 并返回 Option<_> 的功能都可以直接与|>>> 组合。

因此,ROP 提高了可读性和可组合性。