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”。有些可疑的事情正在发生。我认为编译器类型推断中存在一个微妙的缺陷。