在组合泛型和非泛型类

时间:2017-01-19 16:01:31

标签: generics types f# static-methods type-inference

我在F#中有一个带有单个类型参数的泛型类,并且想要创建一个包含工厂方法的静态类。当我编写类时,F#编译器会生成与“转换其范围的类型变量”相关的错误。我的问题是为什么错误存在以及如何解决它。

我已经创建了一个最小尺寸代码段来演示此问题:

type Foo<'a>(element : 'a) =

    member this.Copy () = Bar.Create(element)

and Bar =

    static member Create(element : 'a) = new Foo<'a>(element)

类型中的相互递归是因为我希望类型Foo<'a>能够在静态类中调用工厂方法。上面的代码片段没有编译,错误是:“类型推断导致类型变量a逃避其范围。考虑添加显式类型参数声明或调整代码以减少通用。”该错误被注册为位于Create类的Bar方法中。不幸的是,我并不是真的了解这个问题,也不知道如何解决它。有什么想法吗?

这是一个额外的观察。摘录

type Foo<'a>(element : 'a) =

    member this.Element = element

and Bar =

    static member Create(element : 'a) = new Foo<'a>(element)

编译。因此,该问题似乎与基于Copy()类的Foo<'a>方法进行的类型推断有关。此外,该片段

type Foo<'a>(element : 'a) =

    member this.Copy () = Bar.Create(element)

and Bar =

    static member Create<'a>(element) = new Foo<'a>(element)

是一个更像C#的代码版本(其中静态方法显式为泛型),它也不编译,错误“此代码不够通用。类型变量'a无法泛化因为它会逃避它的范围。“

3 个答案:

答案 0 :(得分:6)

递归成员的类型推断通常需要至少对某些定义进行类型注释。但是,有时您可以通过重新排序定义来避免这种情况,至少可以在简化的复制中使用:

type Bar = 
    static member Create(element) = Foo(element)
and Foo<'a>(element:'a) =
    member this.Copy() = Bar.Create(element)

(请注意,我甚至删除了elementBar.Create上的注释。

我不知道有一个易于理解的解释,不幸的是,在任何特定情况下都需要注释。

答案 1 :(得分:4)

这似乎无需Bar通用:

type Foo<'a>(element : 'a) =
    member this.Copy () = Bar.Create element
and Bar =
    static member Create<'a>(element : 'a) : Foo<'a> = Foo(element)

Online Demo

不知道为什么,只是通过反复试验找到了。

答案 2 :(得分:2)

我实际上看到了一个不同的错误,关于类型变量'a尚未解决,我可以通过使用'a参数化Bar来解决它:

type Foo<'a>(element : 'a) =
    member this.Copy () = Bar.Create(element)

and Bar<'a> =
    static member Create(element : 'a) = new Foo<'a>(element)

我担心我没有很好的解释为什么在你有相互递归类型的场景中需要这个,而不是你有一个单独的Bar类型。

我倾向于避免相互递归的类型 - 你不能没有它们的情况很少见。大多数情况下,您可以重新构建代码以避免递归,并且通常最终会得到一些在必要时更容易阅读和重构的内容。