選件模組支援鐵路定向程式設計

錯誤處理很重要,但可以使優雅的演算法陷入混亂。鐵路定向程式設計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 提高了可讀性和可組合性。