5.3.4 F# 中使用选项(option)类型

5.3.4 F# 中使用选项(option)类型

 

    我们常常需要描述这样的理念,某些计算可能会返回未定义的值。在 C# 中,通常用返回 null(空值)实现。不幸的是,使用 null 频繁导致错误:可能轻松地编写代码,假定方法不返回空,当这种假设是错误的,可以看到臭名昭著的 NullReference 异常。当然,正确编写的代码始终检查 null 值在适当的情况下, 当编写应用程序的单元测试,大量的试验验证和在边界情况下。

    在 F# 中,空值的使用最小化,它通常只用于与 .NET 类型进行互操作的情况下。为了表示可能返回未定义结果的计算,我们使用的选项类型。当我们以此作为函数的返回类型,这是一个显式的声明,结果可能是未定义的;这也可以让编译器强制调用方去处理一个未定义的结果。

    选项类型是具有两个可选值的差别联合。识别器 Some 用于创建一个选项,携带一个值;None 用于表示未定义的值。清单 5.7 显示一个函数,从控制台读取一个输入,当用户没有输入数字时,返回未定义的值。

 

Listing 5.7 Reading input as an option value (F# Interactive)

> open System;;

> let readInput() =
    let s = Console.ReadLine()
    match Int32.TryParse(s) with
    | true, parsed -> Some(parsed)
    | _ -> None
;;
val readInput : unit -> int option

 

    代码是相当简单:它读取输入,使用 TryParse 方法解析它,使用该选项类型中的一种情况构造返回值。我们使用一个有趣和强大的模式匹配来实现的功能方面。输入匹配构造是 TryParse 方法所返回的元组。当解析成功,元组的第一个值将是真,第二个值正是我们感兴趣的数字。要在模式匹配内部处理这种情况,我们指定真常量作为第一个模式,新的值 parsed 为第二个模式。当元组的第一个元素是真时,模式匹配把分析的数字赋给值 parsed,使用 Some 返回这个结果。

    第二个分支使用下划线模式来处理所有剩余的情况。在这种情况下,我们知道解析失败,所以,我们使用 None 返回未定义的结果。还可以查看由 F#  Interactive 打印出的签名,说是该方法返回 int option,这意味着,该选项类型是泛型,并在这种情况下,携带一个整数值。我们会在第 5.4.2 节中看到这样的泛型类型是如何定义的。

    首先,让我们看一下使用此函数的代码。这里,我们看到使用选项类型的真正好处,语言会强制我们写代码来处理未定义值。这是因为访问值的唯一方法是通过使用模式匹配。可以在清单 5.8 中看到这个示例。

 

Listing 5.8 Processing input using the option type (F# Interactive)

 

> let testInput() =
     let input = readInput()
     match input with
     | Some(number) –>
       printfn "You entered: %d" number
     | None –>
       printfn "Incorrect input!";;
val testInput : unit –> unit

> testInput();;
42
You entered: 42

> testInput();;
fortytwo
Incorrect input!

 

    如你所见,我们不能在调用 readInput 函数后直接使用这个值,这是关键的区别,它是使程序更安全,因为,当一个函数返回空值时,不必检查这种可能性。要在 F# 中读取这个值,必须使用模式匹配,我们为每个选项类型情况写了一个分支。我们已经看到过,F# 会验证模式匹配是否完整,即,是否涵盖了所有可能的选项。这可以保证我们不会意外地写的代码只包含 Some 识别器分支。清单 5.8 也遵循 F# 的最佳做法,通过在 F # Interactive 中立刻测试代码,检查它的行为在这两种情况都正确。

 

可空(Nullable)和选项类型

 

    F# 的选项类型在某些方面类似于 C# 中的 Nullable 类型,但是,更通用、更安全。在 C# 中,当我们想要表示一个缺失值(missing value),通常使用 Null,但这是只可能用于引用类型。可空类型可用于创建值类型,它也有一个有效的值 null。

    在 F# 中,null,不是任何在 F# 中声明的类型的有效值(尽管,对于现有的 .NET 引用类型,它仍然有效值)。这意味着,只要我们需要创建任何可能是空的值,要把实际类型包装到选项类型中。多亏有了模式匹配,编译器也能够确保我们实现的代码,始终可以处理缺失值的情况。

 

    现在,我们已经看到了如何使用选项类型,以及它们对于 F# 编程是何等重要,那么,我们就讨论如何实现。

 

实现 F# 中简单选项

 

    在前面的示例中,我们使用了一个携带整数的选项类型,所以,让我们先看看简单一点的类型,IntOption,它只能携带整数值。我们确保你已经可以自己写出这个类型的声明,如这里:

 

> type IntOption =
     | SomeInt of int
     | NoneInt;;
(...)

> SomeInt(10);;
val it : IntOption = SomeInt 10

 

    在我们的声明和来自的 F# 库的选项类型之间,有一个大区别:库类型是泛型的,这意味着,可以使用它来存储任何类型的值,其中,包括 .NET 对象的引用,比如,Some(new Button())。编写泛型类型是很重要的,因为它会使代码适用性更广。现在让我们近距离地看一看。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值