F# 如何从其他模块推断类型和标签?
How does F# infer types and tags from other modules?
这是我用来解释我的问题的最小代码示例。以下代码被组织在两个文件中并编译正常:
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
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
有趣的是,我有一个 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
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
现在 Program.fs 包含两个错误。在我尝试调用 GetNew 的两行中,它说:"The field, constructor or member 'GetNew' is not defined"
这是因为 MyMod.XXX 被推断为 MyMod.Choice.
类型的联合案例
现在,在不更改代码的任何结构的情况下,我只需将 Choice 标签重命名为不同于它们所代表的类型,一切又恢复正常了。
DataStruct.fs 同上,但
type Choice =
| TX of XXX
| TY of YYY
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
现在对 GetNew 的调用是合法的,因为 MyMod.XXX 被正确推断为我打算使用的类型。
现在的问题是:上述问题是F#的错误还是特性?
或者换句话说,虽然建议对标签及其类型使用相同的名称,但这似乎是类型推断机制的问题。那么这个建议是错误的还是我以错误的方式使用名称空间、模块、类型和标签?
你的第一段代码和第二段代码的区别在于你如何在 Program.fs
中打开模块:
在您的第一个代码中,通过编写 open MyMod
,您可以打开模块
在您的第二个版本中,通过编写 open DataStruct
,您仅打开命名空间,但 尚未 模块。如果将其更改为 open DataStruct.MyMod
,您将获得与第一个版本完全相同的行为。
我对发生的事情的粗略解释:
- 打开模块后,F# 会看到两个
XXX
浮动,并且能够根据使用来消除歧义。
- 当您使用模块名称进行限定时,您将
XXX
限制为 MyMod
中定义的最新类型 XXX
。第一个 XXX
是您的记录,第二个是从 Choice
派生的 class,也称为 XXX
。例如,在 ILSpy 中查看您的程序集。
更新:第二段不正确。当使用模块名称限定时,F# 编译器错误地将 XXX
限制为 DU 类型,从而隐藏记录类型。详情请看我的第二个回答。
此行为是编译器中的错误。该问题与编译器中的另一个错误有关,其中受歧视的联合类型正在隐藏同一模块中的其他类型定义,see this bug report. In the code you had posted: The root cause of the bug is here in the name resolution. MyMod.XXX
is identified as a valid expression that refers to a DU type. This search is done greedily, the codepath that searches for alternative resolutions 未执行。
我已提交错误报告in visualfsharp and fsharp
这是我用来解释我的问题的最小代码示例。以下代码被组织在两个文件中并编译正常:
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
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
有趣的是,我有一个 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
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
现在 Program.fs 包含两个错误。在我尝试调用 GetNew 的两行中,它说:"The field, constructor or member 'GetNew' is not defined" 这是因为 MyMod.XXX 被推断为 MyMod.Choice.
类型的联合案例现在,在不更改代码的任何结构的情况下,我只需将 Choice 标签重命名为不同于它们所代表的类型,一切又恢复正常了。
DataStruct.fs 同上,但
type Choice =
| TX of XXX
| TY of YYY
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
现在对 GetNew 的调用是合法的,因为 MyMod.XXX 被正确推断为我打算使用的类型。
现在的问题是:上述问题是F#的错误还是特性? 或者换句话说,虽然建议对标签及其类型使用相同的名称,但这似乎是类型推断机制的问题。那么这个建议是错误的还是我以错误的方式使用名称空间、模块、类型和标签?
你的第一段代码和第二段代码的区别在于你如何在 Program.fs
中打开模块:
在您的第一个代码中,通过编写
open MyMod
,您可以打开模块在您的第二个版本中,通过编写
open DataStruct
,您仅打开命名空间,但 尚未 模块。如果将其更改为open DataStruct.MyMod
,您将获得与第一个版本完全相同的行为。
我对发生的事情的粗略解释:
- 打开模块后,F# 会看到两个
XXX
浮动,并且能够根据使用来消除歧义。 - 当您使用模块名称进行限定时,您将
XXX
限制为MyMod
中定义的最新类型XXX
。第一个XXX
是您的记录,第二个是从Choice
派生的 class,也称为XXX
。例如,在 ILSpy 中查看您的程序集。
更新:第二段不正确。当使用模块名称限定时,F# 编译器错误地将 XXX
限制为 DU 类型,从而隐藏记录类型。详情请看我的第二个回答。
此行为是编译器中的错误。该问题与编译器中的另一个错误有关,其中受歧视的联合类型正在隐藏同一模块中的其他类型定义,see this bug report. In the code you had posted: The root cause of the bug is here in the name resolution. MyMod.XXX
is identified as a valid expression that refers to a DU type. This search is done greedily, the codepath that searches for alternative resolutions 未执行。
我已提交错误报告in visualfsharp and fsharp