简体   繁体   English

展开F#单例区分联合元组类型

[英]Unwrap F# single-case discriminated union tuple type

We can unwrap type like type Address = Address of string using unwrapping function like 我们可以像使用展开函数一样打开类型type Address = Address of string

let unwrapAddress (Address a) = a
let addr = Address "sdf"
let str = unwrapAddress addr

so str will be of type string , but if there is type like this approach willn't work: 所以str将是string类型,但是如果有这样的类型方法将不起作用:

type Composite = Composite of integer:int * someStr:string
let unwrap (Composite c) = c

will produce error 会产生错误

let unwrap (Composite c) = c;;
------------^^^^^^^^^^^
error FS0019: This constructor is applied to 1 argument(s) but expects 2

Can I somehow unwrap composite types to a simple tuple? 我可以以某种方式将复合类型解包为一个简单的元组吗?

You defined the type as a single-case discriminated union with named fields: 您将类型定义为具有命名字段的单个案例区分联合:

type Composite = Composite of integer:int * someStr:string

When defined in this way, the fields of the union case are not a simple tuple. 以这种方式定义时,union case的字段不是简单的元组。 They get treated in a special way and, for example, the names are used as property names in compiled code. 它们以特殊方式处理,例如,名称在编译代码中用作属性名称。 The pattern matching does not automatically turn the elements into a tuple and so you have to unwrap them separately: 模式匹配不会自动将元素转换为元组,因此您必须单独打开它们:

let unwrap (Composite(i, s)) = i, s

However, you can also define single-case union where the field is an ordinary tuple. 但是,您也可以定义单个案例并集,其中字段是普通元组。 (Note that you need the parentheses around the tuple type - otherwise, it also ends up being treated in a special way, except that the items will be compiled as Item1 and Item2 .) (请注意,您需要围绕元组类型的括号 - 否则,它最终也会以特殊方式处理,除了项目将被编译为Item1Item2 。)

type Composite = Composite of (int * string)

With this definition, your unwrap function will work fine and extract the tuple value: 使用此定义,您的unwrap函数将正常工作并提取元组值:

let unwrap (Composite c) = c

You can also use a nested pattern to get the number and the string like in the previous case: 您还可以使用嵌套模式来获取数字和字符串,如上例所示:

let unwrap (Composite(i, s)) = i, s

The fact that this behaves differently depending on whether you write A of (T1 * T2) or whether you write A of T1 * T2 is a bit subtle - the two probably need to be distinguished just so that the compiler knows whether to compile the fields as two separate fields or as one field of type System.Tuple<T1, T2> . 根据您是否编写A of (T1 * T2)或者是否编写A of T1 * T2 ,这种情况会有所不同,这两者可能需要区分,以便编译器知道是否编译字段作为两个单独的字段或作为System.Tuple<T1, T2>类型的一个字段。 I cannot quite imagine any other case where the difference would matter. 我无法想象任何其他情况下差异会很重要。

These all work for me. 这些都适合我。 It's your matching syntax, that most often you'll find used with match statements, but it's on the lhs of an assignment. 这是你的匹配语法,通常你会发现它与匹配语句一起使用,但它是在赋值的lhs上。 Possibly, this makes the most sense, initially, for tuples, but you can use this with any structure. 可能,这对于元组来说最有意义,但是你可以在任何结构中使用它。

let (a,b) = (1,2)

let (x,_) = (4,5)

Two other interesting things to try: 另外两个有趣的事情:

let (head::tail) = [1;2;3;4]

FSI responds warning FS0025: Incomplete pattern matches on this expression. FSI响应警告FS0025:此表达式上的不完整模式匹配。 For example, the value '[]' may indicate a case not covered by the pattern(s). 例如,值“[]”可以指示模式未涵盖的情况。

"That's true," you reason aloud. “那是真的,”你大声说。 "I should express it as a match and include an empty list as a possibility". “我应该把它表达为一个匹配,并列出一个空列表作为一种可能性”。 It's better to bubble these kinds of warnings into fully bonafide errors (see: warn as error eg --warnaserror+:25 ). 最好将这些类型的警告冒充为完全真实的错误(参见: 警告错误,例如--warnaserror +:25 )。 Don't ignore them. 不要忽视它们。 Resolve them through habit or the compiler enforced method. 通过习惯或编译器强制方法解决它们。 There's zero ambiguity for the single case, so code-on. 单个案例没有歧义,所以代码开启。

More useful + interesting is the match syntax on the lhs of a function assignment. 更有用+有趣的是函数赋值的lhs上的匹配语法。 This is pretty cool. 这很酷。 For pithy functions, you can unpack the stuff inside, and then do an operation on the internals in one step. 对于精辟的函数,您可以解压缩内部的东西,然后在一个步骤中对内部进行操作。

let f (Composite(x,y)) = sprintf "Composite(%i,%s)" x y

f (Composite(1,"one"))

> val it : string = "Composite(1,one)"

About your code: 关于你的代码:

type Address = Address of string //using unwrapping function like

let unwrapAddress (Address a) = a
let addr = Address "sdf"
let str = unwrapAddress addr

type Composite = Composite of integer:int * someStr:string
let unwrap (Composite(c,_)) = c
let cval = Composite(1,"blah")
unwrap cval

Workaround: 解决方法:

let xy = Composite(1,"abc") |> function (Composite(x,y))->(x,y)

... but the nicer way, assuming you want to keep the named elements of your single case DU would be... ...但更好的方式,假设你想保留你的单个案例DU的命名元素将...

let (|Composite|) = function | Composite(x,y)->(x,y)

let unwrap (Composite(x)) = x

let unwrap2 (Composite(x,y)) = (x,y)

... not strictly decomposing through a single case DU, but decomposing through a single-case Active Pattern ...不是通过单个案例DU严格分解,而是通过单个案例的活动模式进行分解

lastly, you could attach a method to the Composite structure... 最后,您可以将方法附加到Composite结构...

module Composite = 
  let unwrap = function | Composite(x,y)->(x,y)

One of the best discussions about using this technique is over here 关于使用这种技术的最佳讨论之一就在这里

Also, check out the signature that unwrap gives us: a function that takes a Composite (in italics), and returns an int (in bold) 另外,查看unwrap给我们的签名:一个复合(斜体)的函数,并返回一个int(以粗体显示)

Signature -- val unwrap : Composite -> int Signature - val unwrap: Composite - > int

In your case, you can write: 在你的情况下,你可以写:

type Composite = Composite of int * string 

let unwrap (Composite (a, b)) = a, b

which corresponds to: 对应于:

let unwrap x = 
    match x with
    | Composite (a, b) -> a, b

What's happening here is that F# allows you to deconstruct function arguments inline using arbitrarily complex pattern matching. 这里发生的是F#允许您使用任意复杂的模式匹配来内联解构函数参数。 This is often mentioned when introducing single case DU's, but it's rarely followed to the conclusion, which leads people to believe single case DU's are somehow special that way. 在引入单个案例DU时经常会提到这一点,但很少有结论,这导致人们相信单个案例DU在某种程度上是特殊的。

In fact, you can use it when you have multiple cases (as long as each case binds the same set of variables): 实际上,当您有多个案例时(只要每个案例绑定同一组变量),您就可以使用它:

type Composite = Composite of int * string | JustString of string

let unwrapString (Composite (_, s) | JustString s) = s

But most of the time, you'd pattern match on simpler types, like tuples: 但大多数时候,你会在更简单的类型上进行模式匹配,比如元组:

let f (a, b, c) = ...

or even more curiously: 甚至更奇怪的是:

let f () = ...

Here () is a pattern match on the lone value of unit type - rather than some kind of "visual marker for a parameterless function", as it's often described. Here ()是单位类型的单独值的模式匹配 - 而不是某种“无参数函数的可视标记”,正如它经常描述的那样。

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

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