繁体   English   中英

为什么F#在更新记录时不推断记录类型?

[英]Why doesn't F# infer the record type when updating a record?

此示例代码:

type recordA = { X: string; }
type recordB = { X: string; }

let modifyX newX record = { record with X = newX }

let modifiedRecordA = {recordA.X = "X"} |> modifyX "X2" 
let modifiedRecordB = {recordB.X = "X"} |> modifyX "X2" 

结果是:

  let modifyX newX record = { record with X = newX }
  --------------------------^^^^^^^^^^^^^^^^^^^^^^^^

stdin(4,27): warning FS0667: The field labels and expected type of this record expression or pattern do not uniquely determine a corresponding record type

  let modifiedRecordA = {recordA.X = "X"} |> modifyX "X2" 
  -------------------------------------------^^^^^^^^^^^^

stdin(6,44): error FS0001: Type mismatch. Expecting a
    recordA -> 'a    
but given a
    recordB -> recordB    
The type 'recordA' does not match the type 'recordB'

我的期望是,modifiedRecordA最终等同于{recordA.X =“ X2”},modifiedRecordB最终等同于{recordB.X =“ X2”},但这似乎并不起作用。

  1. 为什么这不只是根据参数类型推断并返回适当的记录类型?
  2. 我有什么可以做的工作吗?

函数modifyX不正确。 您可能没有在定义中使用术语X ,而是让X引用了不同的字段。

F#规范的第6.3.6节

每个字段标签必须解析为单个记录类型R中的字段Fi,所有字段都可以访问。

通过将recordArecordB传递给modifyXX不会唯一地确定为单一类型的字段。

您真正想要的可能是通过接口继承的多态属性成员,而不是一组具有公共成员名的记录类型。

问题在于,编译器根据使用情况推断了modifyX的类型。 我的理解是,这是自下而上进行的,因此推断类型为val modifyX : newX:string -> record:recordB -> recordB 当尝试将它与recordA类型的记录一起使用时,这当然会导致类型错误。 警告告诉您,尽管它选择了一个类型,但还有另一个具有相同字段的类型,因此编译器可以做的就是尽最大可能猜测出您要输入的类型。 可能可以使用内联函数实现您要执行的操作,但是我不确定这可能会立即起作用。

进行此工作所需的内联魔术基于过载解析和静态解析的成员约束 定义为运算符的重载避免了阐明明确的约束的需要。

type Foo = Foo with
    static member ($) (Foo, record) = fun newX -> { record with recordA.X = newX }
    static member ($) (Foo, record) = fun newX -> { record with recordB.X = newX }

let inline modifyX newX record = (Foo $ record) newX

let modifiedRecordA = {recordA.X = "X"} |> modifyX "X2" 
let modifiedRecordB = {recordB.X = "X"} |> modifyX "X2"

传递不存在重载的类型的构造不编译。

type recordC = { X: string; }
let modifiedRecordC = {recordC.X = "X"} |> modifyX "X2" 
// Error    No overloads match for method 'op_Dollar' ...
// Possible overload ... The type 'recordC' is not compatible with the type 'recordB'
// Possible overload ... The type 'recordC' is not compatible with the type 'recordA'

这并不是真正的实际用途。 注意建议并探索其他方法是否更适合您的问题。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM