f# - 在有区别的联合案例中使用元组时,括号的作用是什么?
问题描述
Microsoft 文档定义的可区分联合语法不包括括号。以下定义允许我在类型定义中使用字段名称,并且与文档中的语法兼容。
type Node<'T> = L of value:'T | N of value:'T * children:Node<'T> list
这个定义:
type Node<'T> = L of value:'T | N of (value:'T * children:Node<'T> list)
给我错误:
Anonymous type variables are not permitted in this declaration
但是,如果我不使用字段名称,括号很好:
type Node<'T> = L of 'T | N of ('T * Node<'T> list)
此更改请求中的评论提到了括号使用之间的差异,但坦率地说,整个讨论都超出了我的想象。
我的印象是,无论我是否使用括号,我都在定义一个元组,即我希望编译器将它们视为冗余,但显然存在差异。它是什么?
解决方案
这只是重载的语法。尽管句法相似,但这两种情况在语义上是不同的。
混淆来自这样一个事实,即定义 DU 案例字段和定义元组类型使用相同的语法。
考虑这两种类型:
type A = A of int * string
type B = B of (int * string)
在这里,构造函数A
不包装元组。相反,它有两个字段 - 第一个字段 type int
,第二个字段 type string
。DU 案例字段可以选择具有名称,这也是它起作用的原因:
type A1 = A1 of x: int * y: string
B
另一方面,构造函数没有两个字段。它只有一个字段,该字段的类型是int * string
. 由于 DU 案例字段可能有名称,我们也可以为该单个字段命名:
type B1 = B1 of t: (int * string)
在构造A
or类型的值时A1
,您在括号中指定所有字段:
let a = A (42, "foo")
就像在类型声明中一样,这看起来像一个元组,但事实并非如此。这是两个独立的字段。由于 DU 案例字段可能有名称,因此您可以在构造值时使用这些名称。有时它有助于不混淆它们或只是为了代码可读性:
let a1 = A1 (x = 42, y = "foo")
事实上,DU 案例字段总是有名称,即使您没有明确指定它们。省略时,编译器将分配看起来像的名称ItemN
(或仅Item
当只有一个字段时)。是的,您可以在构造值时使用它们:
let a = A (Item1 = 42, Item2 = "foo")
另一方面,在构造 的值时B
,不能为元组元素使用名称,因为元组元素没有名称:
let b = B (42, "foo") // works
let b = B (Item1 = 42, Item2 = "foo") // doesn't compile
但是您可以使用该单个字段的名称,其类型为int * string
:
let b = B (Item = (42, "foo"))
let b1 = B1 (t = (42, "foo"))
它与模式匹配类似。考虑表达式:
match a with
| A (p, q) -> ...
再说一次,尽管(p, q)
看起来像一个元组,但它不是。它与构造函数内的元组不匹配A
。相反,它匹配构造函数的两个字段。没有一个元组。两个领域。
与构造值类似,您可以使用字段名称:
match a with
| A1 (x = p, y = q) -> ...
使用字段名称还可以让您部分匹配:
match a with
| A1 (x = p) -> ...
但是当你匹配时B
,你有两个选择:
(1) match b with B (x, y) -> ...
(2) match b with B t -> ...
第二个选项的工作原理与 match 相同A
:构造函数B
有一个字段,我们正在匹配该字段。
第一个选项有效,因为模式可以嵌套:我们匹配构造函数的单个字段B
,然后我们匹配同一模式中该字段的各个元素。
当然,您也可以使用字段名称:
match b with B (Item = (x, y)) -> ...
match b1 with B1 (t = (x, y)) -> ...
也许不幸的是,DU case 字段的语法与元组的语法如此相似,以至于造成了如此多的混乱,但我们在这里。要记住的底线是:这些不是元组,它只是一个类似的语法。
推荐阅读
- c++ - LNK2019 调用 .cpp 文件中静态成员的方法时出错
- mysql - Laravel Eloqunt 基于条件的多选
- c++ - 在这种情况下如何防止 C++ 输出/控制台表单关闭
- laravel-5 - Laravel 5 - 重定向
- node.js - 使用节点 js 中的 findOneAndUpdate 更新数组中的对象
- angular - 导出所有管道的 Angular 模块是否支持摇树?
- python - 像关键字一样表达方法
- html - 用于不同定位方案的 HTML/CSS 中的坐标系
- java - 如何设置 JavaFX TextField 的样式以使其看起来像 iOS?
- matplotlib - 如何删除 matplotlib 中的颜色图?