Types F#如何从其他模块推断类型和标记?
下面是一个我用来解释我的问题的最小代码示例。以下代码分为两个文件并编译: DataStruct.fsTypes F#如何从其他模块推断类型和标记?,types,f#,type-inference,f#-4.0,Types,F#,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 = -
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
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
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
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
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
有趣的是,我有一个Choice类型,它有两个标记,它们的命名方式与它们所标记的类型相同。据我所知,这是F#中的一个常见惯例
现在我更改了DataStruct,以便将其放在名称空间中,并使MyMod成为该名称空间中的模块之一。因此,在Program.fs中,我打开名称空间并使用以模块名称为前缀的所有内容:
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
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
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
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
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
现在Program.fs包含两个错误。在我试图调用GetNew的两行代码中,它说:“字段、构造函数或成员“GetNew”未定义”
这是因为MyMod.XXX被推断为MyMod.Choice类型的联合案例
现在,在不改变任何代码结构的情况下,我只需将Choice标记重命名为不同于它们所表示的类型,一切都会恢复正常
DataStruct.fs如上所述,但带有
type Choice =
| TX of XXX
| TY of YYY
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
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
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
现在对GetNew的调用是合法的,因为MyMod.XXX被正确地推断为我打算使用的类型
现在的问题是:上面描述的问题是F#的bug还是特性?
或者换言之,虽然建议对标记及其类型使用相同的名称,但类型推断机制似乎存在问题。那么,这个建议是错误的,还是我使用名称空间、模块、类型和标记的方式不正确?第一段代码和第二段代码的区别在于如何在
程序中打开模块。fs
:
- 在第一个代码中,通过编写
,可以打开模块openmymod
- 在第二个版本中,通过编写
,您只打开名称空间,但还没有打开模块。如果将其更改为opendatastruct
,您将获得与第一个版本完全相同的行为打开DataStruct.MyMod
- 当模块打开时,F#看到两个
浮动,并且能够根据使用情况消除歧义李>XXX
- 当您符合模块名称的条件时,您将
限制为XXX
中定义的最新类型MyMod
。第一个XXX
是您的记录,第二个是从XXX
派生的类,也称为Choice
。例如,在ILSpy中查看您的程序集XXX
更新:第二段不正确。当使用模块名限定时,F#编译器错误地将
XXX
限制为DU类型,从而隐藏记录类型。有关更多详细信息,请参阅我的第二个答案。此行为是编译器中的一个错误。该问题与编译器中的另一个bug有关,其中受歧视的联合类型隐藏在同一模块中的其他类型定义上。在您发布的代码中:错误的根本原因在于名称解析MyMod.XXX
被标识为引用DU类型的有效表达式。此搜索是贪婪地完成的,不会执行
我已经提交了错误报告和关于您解释的第二点:在“let myVal:MyMod.XXX”行中,XXX被正确识别为类型,这与只有后来声明的MyMod.Choice.XXX才是XXX的类型相矛盾。您是对的,这与我的解释不一致。让我再挖一点。谢谢你的努力,我希望这会很快得到解决。我有点惊讶的是,没有更多的人在这一点上感到困惑,因为我相信这种对歧视性结合的使用在F#中会经常出现。@FriedrichGretz:谢谢你的确认。对赏金满意吗?