简体   繁体   English

F# 中的选项类型如何工作

[英]How does the option type work in F#

So I've been reading the Expert F# book by Apress, mostly using it as a reference when building a toy-ish F# library, but there's one thing I've failed to grasp and that's the "Option" type.所以我一直在阅读 Apress 的 Expert F# 书,主要是在构建玩具式 F# 库时将其用作参考,但有一件事我没能掌握,那就是“选项”类型。

How does it work and what is it's real world usage?它是如何工作的,它在现实世界中的用途是什么?

The option type is at least similar to Nullable<T> and reference types in C#.选项类型至少类似于Nullable<T>和 C# 中的引用类型。 A value of type Option<T> is either None which means there's no encapsuated value, or Some with a particular value of T . Option<T>类型的值要么是None ,这意味着没有封装值,要么是Some具有特定值T This is just like the way a Nullable<int> in C# is either the null value, or has an associated int - and the way a String value in C# is either a null reference, or refers to a String object. This is just like the way a Nullable<int> in C# is either the null value, or has an associated int - and the way a String value in C# is either a null reference, or refers to a String object.

When you use an option value, you generally specify two paths - one for the case where there is an associated value, and one where there isn't .当您使用选项值时,您通常会指定两条路径 - 一条用于关联值的情况,另一条用于没有关联值的情况。 In other words, this code:换句话说,这段代码:

let stringLength (str:Option<string>) =
  match str with
  | Some(v) -> v.Length
  | None -> -1

is similar to:类似于:

int StringLength(string str)
{
    if (str != null)
    {
        return str.Length;
    }
    else
    {
        return -1;
    }
}

I believe the general idea is that forcing you (well, nearly) to handle the "no associated value/object" case makes your code more robust.我相信一般的想法是强迫你(好吧,几乎)处理“没有关联的值/对象”的情况会使你的代码更加健壮。

One of the best examples of real-world usage is for the TryParse pattern in.Net.实际使用的最佳示例之一是.Net 中的TryParse模式。 See the first half of看前半部分

http://lorgonblog.spaces.live.com/blog/cns.701679AD17B6D310!181.entry http://lorgonblog.spaces.live.com/blog/cns.701679AD17B6D310!181.entry

for a discussion.进行讨论。

It's used when a function or method should "maybe" or "optionally" return a value.当 function 或方法应该“可能”或“可选地”返回值时使用它。 In C# you'd probably return null, or return a Null Object or possibly a Nullable for value types.在 C# 中,您可能会返回 null,或者返回Null Z497031794414A5类型 5435F90151AC3

The downside of returning null (the most common case) is that it's not type safe: null is an instance of all types, so you get into all kinds of hairy null reference situations later on.返回 null(最常见的情况)的缺点是它不是类型安全的:null 是所有类型的实例,因此您稍后会遇到各种毛茸茸的 null 参考情况。

The Option type is a so called disciminated union type with two constructors: None and Some a. Option 类型是所谓的有区别的联合类型,有两个构造函数:None 和 Some a。 None explicitly indicates that you don't have a value. None 明确表示您没有值。 Basically it's the Null Object pattern generalized.基本上它是广义的 Null Object 模式。

You use it when a value is optional.当值是可选的时使用它。 One use is to have a kind of 'null reference', eg一种用途是拥有一种“空引用”,例如

 val x : int option ref = None 

Then you can later update x to Some v. You use it with the match statement, eg然后您可以稍后将 x 更新为 Some v。您可以将它与 match 语句一起使用,例如

 match !x with (* dereference x *)
    None -> (* No value, do something *)
 |  Some v -> (* Value v, do something else *)

To add to the other answers, the Option type is nothing special -- it's just another discriminated union.添加到其他答案中, Option 类型没什么特别的——它只是另一个有区别的联合。 You could define it yourself in one line:您可以在一行中自己定义它:

type 'a Option = None | Some of 'a

The utility, as others have pointed out, is that pattern matching will let you safely deconstruct this, instead of checking for null or using some hack-workaround to indicate if a value isn't really a value.正如其他人指出的那样,该实用程序是模式匹配将让您安全地解构它,而不是检查 null 或使用一些黑客解决方法来指示一个值是否真的不是一个值。

A functional pattern using the option type:使用选项类型的功能模式:

When you need to change parts of a recursive data structure, such as a tree or a list, you will want to reuse as much of the existing data structure as possible.当您需要更改递归数据结构的一部分时,例如树或列表,您将希望尽可能多地重用现有数据结构。 The option type can help you with this.选项类型可以帮助您解决此问题。 These two functions both replaces all occurrences of the number 5 with 7, but the first one copies the entire tree.这两个函数都将所有出现的数字 5 替换为 7,但第一个函数复制了整个树。 The second one does not.第二个没有。

type Tree = Leaf of int
      | Node of Tree * Tree

let rec replace_no_sharing tree =
    match tree with
    | Leaf 5      -> Leaf 7
    | Leaf x      -> Leaf x
    | Node (a, b) -> Node (replace_no_sharing a, replace_no_sharing b)

let replace_with_sharing tree =
    let rec replace_option tree =
        match tree with
        | Leaf 5      -> Leaf 7 |> Some
        | Leaf x      -> None
        | Node (a, b) -> match replace_option a, replace_option b with
                         | None, None     -> None
                         | Some a, Some b -> Node (a, b) |> Some
                         | Some a, None   -> Node (a, b) |> Some
                         | None, Some b   -> Node (a, b) |> Some
    match replace_option tree with
    | None      -> tree
    | Some tree -> tree

It might not be necessary in all cases, but it's a nice technique to know.可能并非在所有情况下都需要它,但这是一种很好的技术。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM