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
Generics F#:在此程序点之前,泛型成员已在非统一实例化中使用_Generics_F# - Fatal编程技术网

Generics F#:在此程序点之前,泛型成员已在非统一实例化中使用

Generics F#:在此程序点之前,泛型成员已在非统一实例化中使用,generics,f#,Generics,F#,我有两个递归定义的类,它们可以按一个顺序编译,但不能按另一个顺序编译。代码的简化版本如下所示: type Loader() = member this.Load path = let ref = Ref() ref.Wait() ref and Ref<'T>(data: obj) = member this.Foo = 1 member this.Wait () = () member this.Va

我有两个递归定义的类,它们可以按一个顺序编译,但不能按另一个顺序编译。代码的简化版本如下所示:

type Loader() =
    member this.Load path =
        let ref = Ref()
        ref.Wait()
        ref

and Ref<'T>(data: obj) =
    member this.Foo = 1
    member this.Wait () = ()
    member this.Value = data :?> 'T
asset.fs(11,21): error FS1198: The generic member 'Wait' has been used at a non-uniform
instantiation prior to this program point. Consider reordering the members so this
member occurs first. Alternatively, specify the full type of the member explicitly,
including argument types, return type and any additional generic parameters
and constraints.
请注意,在简化版本中,确实不需要递归-但是,实际代码确实需要这两个声明是递归的


我可以通过指定Wait()的类型来解决这个问题,如错误消息中所述(即member
this.Wait():unit=()
)-但是,我想了解我为什么需要这样做。

为了回答您的问题,我引用@Brian的话:

通常,“修复”只是添加一个完整类型的签名(包括 返回类型)到某个“声明延迟”但“调用 早期(在成员集合中引入正向引用)

在相互递归类的情况下,当调用
ref.Wait()
时,类型推断将查看
ref
的类型声明及其解析
Wait
类型签名的第一个成员。由于此时没有可用的信息,它错误地将
Wait
的签名推断为
unit->'a
。根据以下示例,查找类的第一个成员是我的观察结果:

type Loader() =    
    member this.Load path =
        let ref = Ref()
        ref.Wait()        
        ref
    member this.Load2 path =
        let ref = Ref()
        ref.Wait2()        
        ref

and Ref<'T>(data: obj) =        
    member this.Wait () = () // resolve correctly
    member this.Wait2 () = () // fail to resolve     
    member this.Foo = 1
    member this.Value = data :?> 'T
类型加载器()=
加载路径=
设ref=ref()
参考文献Wait()
裁判
成员路径为this.Load2=
设ref=ref()
参考文献2()
裁判

Ref我对类型检查的了解还不够,无法解释这种行为(我也不认为它能解释这一点,因为它在第120页只简单地提到了“递归类型”)

无论如何,该规范说编译器将递归类型定义作为单个组进行检查,因此使用
Wait
等方法(部分)推断其类型。当显式指定类型时,显式定义将覆盖推断的类型

在您的示例中,问题仅在某些
'T
Ref
时出现


根据,只有在处理
Wait
方法的定义时,添加与
变量相关的约束失败(第8914行)时,才会报告错误。因此,我的猜测是,与
Load
中的
ref
值相关的一些约束稍后会与
Wait
生成的一些约束发生冲突(当编译器开始检查该方法,并且没有确定类型的完整说明时)。

这看起来像是一个bug吗?或者更确切地说是类型系统中真正的不完整性?但我真的不明白为什么不能推断出该类型。我认为Wait()在调用站点被推断为unit->'a,请参阅接受的答案和我对它的评论。@zeuxcg我很确定它在某种程度上与
这个
引用有关,但我真的不明白如何推断(但那是在编译器代码中报告错误的时候). F#可以根据计算顺序推断返回类型为
unit
——如果你写
让rec foo a=bar a;1和bar a=a
,然后推断
bar
返回
unit
(来自
foo
中的使用)。@TomasPetricek是的,这也是我所想的-但是,将这个.Wait()表达式的类型显式指定为unit解决问题没有意义…问题是,在调用位置的第一个推断中等待的类型是什么。我希望它是unit->unit(参见上面的Tomas答案),而不是unit->'a。刚刚选中-将调用代码更改为ref.Wait():unit也解决了问题,所以您必须是正确的-Wait类型被推断为unit->'a,然后通过声明约束为unit->unit,这显然是不允许的。谢谢@zeuxcg:值得注意的是,将行更改为
let()=ref.Wait2()
并不能修复错误,尽管此构造的约束力不应小于显式的“:unit”。有些可疑的事情正在发生。我认为编译器类型推断中存在一个微妙的缺陷。