在以下代码段中
type myrec1 = {x: int; y: int}
type myrec2 = {x: int; y: int; z: int}
let p1 = {x = 1; y = 1} // Error. p1 compiler assumes p1 has the type myrec2
// It works with additional type specification
let p1: myrec1 = {x = 1; y = 1}
let p2: myrec2 = {x = 1; y = 1; z = 1}
Run Code Online (Sandbox Code Playgroud)
带注释的行无法编译.由于某种原因,类型检查器无法确定p1的类型应该是myrec1.是因为这种类型推断的情况根本无法解决,还是只是F#类型推断的限制?
从这里:
最近声明的类型的标签优先于先前声明的类型的标签
所以,如果你这样做:
type myrec2 = {x: int; y: int; z: int}
type myrec1 = {x: int; y: int}
let p1 = {x = 1; y = 1}
Run Code Online (Sandbox Code Playgroud)
然后它会工作.
为了您的阅读快感从这里(F#的3.0规格):
field-initializer : long-ident = expr
Run Code Online (Sandbox Code Playgroud)
6.3.5记录表达式
在这种情况下,我们的field-initializer不是单个标识符,因此它使用14.1.9中的"Field Label Resolution".
每个field-initializeri的格式为field-labeli = expri.每个field-labeli都是一个long-ident,它必须解析为唯一记录类型R中的字段Fi,如下所示:
·如果field-labeli是单个标识符fld并且已知初始类型是具有名称为fld的字段Fi 的记录类型R < ,..., >,则字段标签将解析为Fi.
·如果field-labeli不是单个标识符,或者初始类型是变量类型,则通过在field-labeli上执行字段标签解析(参见§14.1)来解析字段标签.此过程产生一组字段FSeti.该集合的每个元素都有相应的记录类型,因此产生一组记录类型RSeti.所有RSeti的交集必须产生单个记录类型R,然后每个字段解析为R中的相应字段.
14.1.9字段标签解析
我们的long-ident是FieldLabel,因此使用8.4.2中描述的FieldLabels表进行查找.
字段标签解析指定如何解析{field1 = expr;中的field1等标识符.... fieldN = expr}.字段标签解析通过以下步骤进行:
1.在Types表和FieldLabels表(第8.4.2节)中查找所有可用类型的所有字段.
2.返回字段声明集.
8.4.2名称解析和记录字段标签
如此处所述,FieldLabels表用于成员的名称解析(14.1).
对于记录类型,记录字段标签field1 ... fieldN将添加到当前名称解析环境的FieldLabels表中,除非记录类型具有RequireQualifiedAccess属性.FieldLabels表中的记录字段标签在成员的名称解析中扮演特殊角色(第14.1节):表达式的类型可以从记录标签中推断出来.例如:type R = {dx:int; dy:int} let fx = x.dx // x被推断为具有类型R在此示例中,查找.dx被解析为字段查找.
14.1.4表达式中的 名称解析此部分似乎有点模糊,但我认为此时它使用名称解析.如最后所述,如果有多个项目,则返回第一个项目.
给定输入long-ident,环境env,以及后续类型参数< ,..., > 的可选计数n,表达式中的名称解析计算包含long-ident < ,...的解释的结果. ., >前缀作为值或其他表达式项,以及残留路径休息.表达式中的名称解析如何进行取决于long-ident是单个标识符还是由多个标识符组成.如果long-ident是单个标识符ident:
1.在ExprItems表中查找ident.返回结果并清空休息.
2.如果在ExprItems表中没有出现ident,请在Types表中查找,使用与n匹配的通用arity(如果可用).返回此类型并清空休息.
3.如果在ExprItems表或Types表中没有出现ident,则失败.
...
如果表达式包含歧义,则表达式中的名称解析将返回该进程生成的第一个结果.
您感兴趣的部分是上面的最后一行:"返回流程生成的第一个结果".