F#如何推断其他模块的类型和标签?

Fri*_*etz 4 f# types type-inference f#-4.0

这是我用来解释我的问题的最小代码示例.以下代码组织在两个文件中并编译正常:

DataStruct.fs

module MyMod 
type XXX = {
    a: int
}
with
    static member GetNew =
        {
            a = -1
        }

type YYY = {
    a: float
}
with
    static member GetNew =
        {
            a = -1.0
        }


type Choice =
    | XXX of XXX
    | YYY of YYY
Run Code Online (Sandbox Code Playgroud)

Program.fs

open MyMod

let generator = 
    let res = XXX.GetNew
    Choice.XXX res

let myVal : XXX = 
    match generator with
    | XXX x -> x
    | _ -> printfn "expected XXX, got sth else!"; XXX.GetNew
Run Code Online (Sandbox Code Playgroud)

有趣的是,我有一个Choice类型,它有两个标签的名称与它们标记的类型相同.据我所知,这是F#中的一个常见惯例.

现在我更改了DataStruct,以便将其放在命名空间中,并使MyMod成为该命名空间中的一个模块.因此,在Program.fs中,我打开命名空间并使用前缀为模块名称的所有内容:

DataStruct.fs

namespace DataStruct

module MyMod =
    type XXX = {
        a: int
    }
    with
        static member GetNew =
            {
                a = -1
            }

    type YYY = {
        a: float
    }
    with
        static member GetNew =
            {
                a = -1.0
            }


    type Choice =
        | XXX of XXX
        | YYY of YYY
Run Code Online (Sandbox Code Playgroud)

Program.fs

open DataStruct

let generator = 
    let res = MyMod.XXX.GetNew
    MyMod.Choice.XXX res

let myVal : MyMod.XXX = 
    match generator with
    | MyMod.XXX x -> x
    | _ -> printfn "expected XXX, got sth else!"; MyMod.XXX.GetNew
Run Code Online (Sandbox Code Playgroud)

现在Program.fs包含两个错误.在我试图调用GetNew的两行中,它说:"字段,构造函数或成员'GetNew'未定义"这是因为MyMod.XXX被推断为MyMod.Choice类型的并集案例.

现在不改变我的代码的任何结构,我只是简单地将Choice标签重命名为与它们所代表的类型不同,并且一切正常.

DataStruct.fs如上所述但有

type Choice =
    | TX of XXX
    | TY of YYY
Run Code Online (Sandbox Code Playgroud)

Program.fs

open DataStruct

let generator = 
    let res = MyMod.XXX.GetNew
    MyMod.Choice.TX res

let myVal : MyMod.XXX = 
    match generator with
    | MyMod.TX x -> x
    | _ -> printfn "expected XXX, got sth else!"; MyMod.XXX.GetNew
Run Code Online (Sandbox Code Playgroud)

现在调用GetNew是合法的,因为MyMod.XXX被正确推断为我打算使用的类型.

现在的问题是:上面描述的问题是F#的错误或特征吗?或者换句话说,虽然建议对标签及其类型使用相同的名称,但似乎是类型推断机制的问题.那么建议是坏的还是我使用命名空间,模块,类型和标签的方式不好?

Ant*_*fer 5

第一段和第二段代码之间的区别在于如何打开模块Program.fs:

  • 在第一个代码中,通过编写open MyMod,打开模块

  • 在您的第二个版本中,通过编写open DataStruct,您只打开命名空间,但尚未打开模块.如果将其更改为open DataStruct.MyMod,则将获得与第一个版本完全相同的行为.

我对发生的事情的粗略解释:

  • 模块打开后,F#看到两个XXX浮动,并能够根据使用消除歧义.
  • 使用模块名称限定时,您将限制为定义XXX的最新类型.第一个是你的记录,第二个是派生自那个的类.例如,看一下像ILSpy这样的程序集.XXXMyModXXXChoiceXXX

更新:第二段是不正确的.使用模块名称限定时,F#编译器会错误XXX地将其限制为DU类型,从而遮蔽记录类型.请参阅我的第二个答案了解更多详情