Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/fsharp/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Types F#如何从其他模块推断类型和标记?_Types_F#_Type Inference_F# 4.0 - Fatal编程技术网

Types F#如何从其他模块推断类型和标记?

Types 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 = -

下面是一个我用来解释我的问题的最小代码示例。以下代码分为两个文件并编译:

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
有趣的是,我有一个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
    派生的类,也称为
    XXX
    。例如,在ILSpy中查看您的程序集

更新:第二段不正确。当使用模块名限定时,F#编译器错误地将
XXX
限制为DU类型,从而隐藏记录类型。有关更多详细信息,请参阅我的第二个答案。

此行为是编译器中的一个错误。该问题与编译器中的另一个bug有关,其中受歧视的联合类型隐藏在同一模块中的其他类型定义上。在您发布的代码中:错误的根本原因在于名称解析
MyMod.XXX
被标识为引用DU类型的有效表达式。此搜索是贪婪地完成的,不会执行


我已经提交了错误报告和

关于您解释的第二点:在“let myVal:MyMod.XXX”行中,XXX被正确识别为类型,这与只有后来声明的MyMod.Choice.XXX才是XXX的类型相矛盾。您是对的,这与我的解释不一致。让我再挖一点。谢谢你的努力,我希望这会很快得到解决。我有点惊讶的是,没有更多的人在这一点上感到困惑,因为我相信这种对歧视性结合的使用在F#中会经常出现。@FriedrichGretz:谢谢你的确认。对赏金满意吗?