【问题标题】:partial deconstruction in pattern-matching (F#)模式匹配中的部分解构 (F#)
【发布时间】:2016-04-08 18:49:54
【问题描述】:

跟随一个观察的最小例子(这让我很惊讶):

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

编辑: 好的,我现在了解所描述行为的“技术”原因(感谢您的回答和 cmets)。但是,我认为语言方面,与其他语言相比,这种行为感觉有点“不自然”:

“代数”,对我来说,区分类型“t”和类型“(t)”似乎很奇怪。括号(在这种情况下)用于给出优先级,例如在“(t * s)* r”与“t *(s * r)”中。 fsi 也会相应地回答,我是否发送

type Vector = (int * int)

type Vector = int * int

对于 fsi,答案总是

类型向量 = int * int

鉴于这些观察,可以得出结论,“int * int”和“(int * int)”表示完全相同的类型,因此在任何一段代码中出现的所有一个都可以用另一个替换(参考透明度)...正如我们所见,这是不正确的。

更重要的是,为了解释手头的行为,我们不得不诉诸“一些代码在编译后的样子”而不是语言的语义属性,因为 imo 表明存在一些“紧张” " 在语言语义和编译器实际执行的操作之间。

【问题讨论】:

  • 尝试使用type Vector = V of (float*float) 来让编译器对内容使用实际的元组。
  • 它有效! ...但它怎么能有所作为...我的意思是类型 Vector = (V of float)*float 甚至无效?
  • 问题是:V of x*y 的意思是“V 拥有 x 和 y 类型的项的元组”还是“V 拥有 x 和 y 类型的两个不同字段”?如果你不给元组加上括号,那么编译器会假定后者,但如果你给它加上括号,那就意味着前者。这种区别主要对与其他 .NET 语言的互操作很重要,但正如您发现的那样,在某些情况下,它也会影响您在 F# 中与此类类型的值进行交互的方式。
  • 感谢您的评论,请参阅我的编辑。

标签: constructor f# pattern-matching


【解决方案1】:

在 F# 中

type Vector = V of float*float

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

type Vector = 
    | V of float*float

of 之后的部分创建两个 anonymous fields(如 F# 参考中所述)和一个接受两个浮点类型参数的构造函数。

如果你定义

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)

第一个例子中没有括号表示联合情况有两个参数。括号 在第二个示例中表明联合情况采用一个参数,该参数是一等元组值。

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

    type Move = 
        | M of (int * int) * (int * int)

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

你使用的其他东西:

type Vector = int * int

是一个type abbreviation,它只是为某种类型命名。在int * int 周围放置括号并没有什么不同,因为这些括号将被视为分组括号。

【讨论】:

  • 我(理解并)同意你所说的一切,我会接受这个答案。但是,我仍然认为在设计方面括号在联合定义中的作用更多的是务实而不是干净/优雅的一面;)。可以有联合定义,其中一些括号用于分组,而其他括号如前所述......形式“......= Y的X”的进一步定义不仅取决于Y“作为一种类型”......
  • 我同意这不是语法上最直观、最简洁的设计选择;另一方面,它也很丑。
猜你喜欢
  • 1970-01-01
  • 2021-02-21
  • 2013-10-19
  • 1970-01-01
  • 1970-01-01
  • 2015-01-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多