F# 在循环移动递归类型时代码中断

F# 在循环移动递归类型时代码中断,f#,F#,在重构某些代码的过程中,我注意到一种情况,即代码在移动时会中断: type ThingBase () = class end and Functions = static member Create<'T when 'T :> ThingBase and 'T : (new : Unit -> 'T)> () = new 'T () and Thing () = inherit ThingBase () static member Create

在重构某些代码的过程中,我注意到一种情况,即代码在移动时会中断:

type ThingBase () = class end

and Functions =
    static member Create<'T when 'T :> ThingBase and 'T : (new : Unit -> 'T)> () = new 'T ()

and Thing () =
    inherit ThingBase ()
    static member Create () = Functions.Create<Thing> ()

// This works, but try moving the Functions type here instead.
不管我怎么做,我都不能把它打印出来。为什么类型推断如此顽固,拒绝泛化
Create
方法

顺便说一句,我还尝试了F#4.1
module rec
,而且
Create
是否是模块中的函数也无关紧要


有什么见解吗?在我看来,这应该是编译器不应该有任何问题的地方。

下面的内容是不正确的。显然递归定义得到两次类型检查——一次用于签名,然后用于实现。不管怎样,我把答案留在这里,仅供参考

原始答案 类型推断从左到右一次完成。一旦遇到对
Functions.Create
的调用,它就决定了签名必须是什么,以后的增强功能无法改变这一点

这与
xs |>Seq.map(fun x->x.Foo)
起作用的原因是一样的,但是
Seq.map(fun x->x.Foo)xs
不起作用:第一种情况下,
x
的类型是从前面遇到的
xs
中知道的,第二种情况下它是未知的,因为还没有遇到
xs


另外,你实际上不需要一个递归组。类型之间没有递归依赖关系。

我不知道为什么编译器在向上移动
Create
方法时过度约束该方法的类型参数。变通方法可以是,因此可以将类型定义拆分为多个部分。这有助于避免递归依赖

type ThingBase () = class end
type Thing () =
    inherit ThingBase ()
type Functions =
    static member Create<'T when 'T :> ThingBase and 'T : (new : Unit -> 'T)> () =
         new 'T ()
type Thing with
    static member Create () = Functions.Create<Thing> ()
type ThingBase()=类结束
类型事物()=
继承ThingBase()
类型函数=
静态成员Create ThingBase和'T:(新建:Unit->'T)>()=
新的'T()
打字
静态成员Create()=函数。Create()

如果您想继续使用该模式,下面介绍如何做到这一点。我假设你想在基地里嵌入某种工厂模式

顺便说一句,@Fyodor表示从左到右,这也意味着自上而下。所以您可能也在反对这一点,尽管和功能在逻辑上应该是有效的。我也同意这样的说法:等级制度更为扁平化,但有时,出于各种原因,我们没有选择的余地

type ThingBase () = class end

and Thing () =
    inherit ThingBase ()

and Functions() =
    static member Create<'T when 'T :> ThingBase and 'T : (new : Unit -> 'T)> () = new 'T ()

and Thing with
    static member Create () = Functions.Create<Thing> ()

// typically, I'd prefer

//type Thing with
//    static member Create () = Functions.Create<Thing> ()

// or

//module Thing = 
//  let Create() = Functions.Create<Thing> ()
type ThingBase()=类结束
还有一件事()=
继承ThingBase()
和功能()=
静态成员Create ThingBase和'T:(new:Unit->'T)>()=new'T()
还有
静态成员Create()=函数。Create()
//一般来说,我更喜欢
//打字
//静态成员Create()=函数。Create()
//或
//模块内容=
//让Create()=函数。Create()
参考资料:


如果您这样做,它将编译

static member Create<'T when 'T :> ThingBase 
         and 'T : (new : Unit -> 'T)> () : 'T = new 'T ()
//                                       ^^^^          
静态成员创建ThingBase
和'T:(new:Unit->'T)>():'T=new'T()
//                                       ^^^^          
其中明确说明了返回类型


递归定义分两次从左到右进行类型检查;首先是函数/方法签名,然后是实体。您需要body(或显式返回类型注释)来获取结果类型,因此您要么需要body先行,要么需要注释以便在两个过程中的第一个过程中得到解决。

如果使用(不必要的)
关键字,它是否像内部类型扩展一样编译?严格来说,不是。我需要检查,但我很确定答案是否定的。我们正在使用与C#类似的扩展语法。而且,在理想的世界里,你不需要工作。然而,如果你因为其他原因领先(想想帕累托原理),那可能没什么大不了的。不过,解开OO思维模式可能需要时间。我也很早就遇到了F#的挫折。
static member Create<'T when 'T :> ThingBase 
         and 'T : (new : Unit -> 'T)> () : 'T = new 'T ()
//                                       ^^^^