选件模块支持铁路定向编程
错误处理很重要,但可以使优雅的算法陷入混乱。铁路定向编程 (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
提高了可读性和可组合性。