選件模組支援鐵路定向程式設計
錯誤處理很重要,但可以使優雅的演算法陷入混亂。鐵路定向程式設計 (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
觀察到我們的程式中有兩種執行路徑
- 快樂的道路 - 最終會計算出
Some
的價值 - 錯誤路徑 - 所有其他路徑生成
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>
只能包含 0
或 1
元素,其中 Option.bind
的行為類似於 List.pick
(概念上 Option.bind
可以更好地對映到 List.collect
但是 List.pick
可能更容易理解)。
bind
,filter
和 map
處理錯誤路徑,g
只包含快樂路徑程式碼。
所有直接接受 Option<_>
並返回 Option<_>
的功能都可以直接與|>
和 >>
組合。
因此,ROP
提高了可讀性和可組合性。