遵循一个最小的观察例子(那种让我感到惊讶的):
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
是什么原因导致无法与部分解构的类型匹配?
一些部分解构似乎没问题:
// Works
let f (x,y) = fst y
编辑: 好的,我现在理解所描述行为的“技术”原因(感谢您的回答和评论)。但是,我认为语言明智,与其他语言相比,这种行为感觉有点“不自然”:
“代数”,对我而言,将“t”类型与“(t)”类型区分开来似乎很奇怪。括号(在此上下文中)用于给出优先权,例如在“(t * s)* r”vs“t *(s * r)”中。此外,fsi会相应地回答,无论我发送
type Vector = (int * int)
或
type Vector = int * int
到fsi,答案总是
输入Vector = int * int
鉴于这些观察结果,可以得出结论:“int * int”和“(int * int)”表示完全相同的类型,因此任何一个代码中的所有出现都可以用另一个替换(参考透明度) )......我们所看到的并不是真的。
此外,为了解释手头的行为,我们不得不求助于谈论“编译后代码如何看起来”而不是语言的语义属性,这表明存在一些“紧张” “在语言语义之间,编译器实际上做了什么。
答案 0 :(得分:1)
在F#中
type Vector = V of float*float
只是一个退化的联合(你可以通过将它悬停在Visual Studio中看到它),所以它相当于:
type Vector =
| V of float*float
of
之后的部分创建了两个anonymous field s(如F#引用中所述)和一个接受float类型的两个参数的构造函数。
如果你定义
type Vector2 =
| V2 of (float*float)
只有一个匿名字段,它是一个浮点元组和一个带有单个参数的构造函数。正如评论中指出的那样,您可以使用Vector2
进行所需的模式匹配。
毕竟,以下代码可能看起来不合逻辑:
let argsTuple = (1., 1.)
let v1 = V argsTuple
但是,如果你考虑到隐藏的模式匹配,那么一切都应该清楚。
编辑:
F# language spec (p 122)明确指出联合定义中的括号问题:
圆括号在联合定义中很重要。因此,以下两个定义不同:
type CType = C of int * int
type CType = C of (int * int)
第一个示例中缺少括号表示union情况有两个参数。括号 在第二个示例中,指示union情况采用一个参数,该参数是一等元组值。
我认为这种行为与你可以在联盟的定义中定义更复杂的模式这一事实是一致的,例如:
type Move =
| M of (int * int) * (int * int)
能够使用带有多个参数的union也很有意义,特别是在互操作情况下,使用元组很麻烦。
您使用的另一件事:
type Vector = int * int
是type abbreviation,它只是为某种类型命名。在int * int
周围放置括号并没有什么区别,因为这些括号将被视为分组括号。