记录类型推断

Max*_*Max 8 f# type-inference

在以下代码段中

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#类型推断的限制?

N_A*_*N_A 9

这里:

最近声明的类型的标签优先于先前声明的类型的标签

所以,如果你这样做:

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,则失败.

...

如果表达式包含歧义,则表达式中的名称解析将返回该进程生成的第一个结果.

您感兴趣的部分是上面的最后一行:"返回流程生成的第一个结果".