模式匹配中的部分解构(F#)

D.F*_*F.F 5 f# constructor pattern-matching

按照观察的最小例子(那种惊讶我):

type Vector = V of float*float

// complete unfolding of type is OK
let projX (V (a,_)) = a

// also works
let projX' x =
   match x with
   | V (a, _) -> a

// BUT:
// partial unfolding is not Ok 
let projX'' (V x) = fst x

// consequently also doesn't work 
let projX''' x =
   match x with
   | V y -> fst y
Run Code Online (Sandbox Code Playgroud)

是什么原因导致无法与部分解构的类型相匹配?

一些部分解构似乎没问题:

// Works
let f (x,y) = fst y
Run Code Online (Sandbox Code Playgroud)

编辑:好的,我现在理解所描述行为的"技术"原因(感谢您的回答和评论).但是,我认为语言明智,与其他语言相比,这种行为感觉有点"不自然":

"代数地",对我来说,将"t"类型与"(t)"类型区分开来似乎很奇怪.括号(在此上下文中)用于给出优先权,例如在"(t*s)*r"vs"t*(s*r)"中.无论我发送,fsi也会相应地回答

type Vector = (int * int)
Run Code Online (Sandbox Code Playgroud)

要么

type Vector = int * int
Run Code Online (Sandbox Code Playgroud)

到fsi,答案总是如此

输入Vector = int*int

鉴于这些观察结果,可以得出结论:"int*int"和"(int*int)"表示完全相同的类型,因此任何一个代码中的所有出现都可以用另一个代码替换(参考透明度).我们所看到的并不是真的.

此外,为了解释手头的行为,我们不得不求助于谈论"编译后代码如何看起来",而不是语言的语义属性,这表明语言之间存在一些"紧张关系".语义是编译器实际执行的操作.

Tom*_*ski 2

在 F# 中

type Vector = V of float*float
Run Code Online (Sandbox Code Playgroud)

只是一个退化的联合(您可以通过将其悬停在 Visual Studio 中来看到),因此它相当于:

type Vector = 
    | V of float*float
Run Code Online (Sandbox Code Playgroud)

之后的部分of创建两个匿名字段(如 F# 参考中所述)和一个接受两个 float 类型参数的构造函数。

如果你定义

type Vector2 = 
    | V2 of (float*float)
Run Code Online (Sandbox Code Playgroud)

只有一个匿名字段,它是一个浮点元组和一个具有单个参数的构造函数。正如评论中指出的,您可以使用它Vector2来进行所需的模式匹配。

毕竟,以下代码的工作原理似乎不合逻辑:

let argsTuple = (1., 1.)
let v1 = V argsTuple
Run Code Online (Sandbox Code Playgroud)

但是,如果您考虑到存在隐藏的模式匹配,那么一切都应该很清楚。

编辑:

F# 语言规范 (p 122)明确指出联合定义中的括号很重要:

括号在联合定义中很重要。因此,以下两个定义有所不同:

type CType = C of int * int

type CType = C of (int * int)

第一个示例中缺少括号表明联合案例需要两个参数。第二个示例中的括号指示 union case 采用一个作为第一类元组值的参数。

我认为这种行为与您可以在联合的定义中定义更复杂的模式这一事实是一致的,例如:

    type Move = 
        | M of (int * int) * (int * int)
Run Code Online (Sandbox Code Playgroud)

能够将联合与多个参数一起使用也很有意义,特别是在互操作情况下,当使用元组很麻烦时。

您使用的另一件事:

type Vector = int * int
Run Code Online (Sandbox Code Playgroud)

是一个类型缩写,它只是为某种类型命名。放置括号int * int不会产生任何影响,因为这些括号将被视为分组括号。