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#的错误或特征吗?或者换句话说,虽然建议对标签及其类型使用相同的名称,但似乎是类型推断机制的问题.那么建议是坏的还是我使用命名空间,模块,类型和标签的方式不好?
第一段和第二段代码之间的区别在于如何打开模块Program.fs:
在第一个代码中,通过编写open MyMod,打开模块
在您的第二个版本中,通过编写open DataStruct,您只打开命名空间,但尚未打开模块.如果将其更改为open DataStruct.MyMod,则将获得与第一个版本完全相同的行为.
我对发生的事情的粗略解释:
XXX浮动,并能够根据使用消除歧义.XXX的最新类型.第一个是你的记录,第二个是派生自那个的类.例如,看一下像ILSpy这样的程序集.XXXMyModXXXChoiceXXX更新:第二段是不正确的.使用模块名称限定时,F#编译器会错误XXX地将其限制为DU类型,从而遮蔽记录类型.请参阅我的第二个答案了解更多详情