【问题标题】:Type variable escapes scope when combining generic and non-generic class结合泛型和非泛型类时,类型变量转义范围
【发布时间】:2017-01-19 16:01:31
【问题描述】:

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

我创建了一个最小尺寸的 sn-p 来演示这个问题:

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

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

and Bar =

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

类型之间存在相互递归,因为我希望类型Foo&lt;'a&gt; 能够调用静态类中的工厂方法。上面的 sn -p 没有编译,错误是:“类型推断导致类型变量 a 逃逸了它的范围。考虑添加显式类型参数声明或调整你的代码不那么通用。”该错误被注册为位于Bar 类的Create 方法中。不幸的是,我并不真正理解这个问题,也不知道如何解决它。有什么想法吗?

这里有一个额外的观察。 sn-p

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

    member this.Element = element

and Bar =

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

编译。所以这个问题似乎与基于Foo&lt;'a&gt; 类的Copy() 方法进行的类型推断有关。此外,sn-p

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

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

and Bar =

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

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

【问题讨论】:

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


    【解决方案1】:

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

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

    (请注意,我什至在Bar.Create 中删除了element 上的注释)。

    不幸的是,我不知道有一个易于理解的解释来说明在任何特定情况下究竟需要哪些注释。

    【讨论】:

      【解决方案2】:

      这似乎可以在不使 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

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

      【讨论】:

      • 这很奇怪。也许Foo 类型和Create 方法上的类型变量之间存在一些相互作用,因为Create 的结果也被推断为没有注释的Foo&lt;'a&gt;。不敢相信它会以这种方式工作。
      【解决方案3】:

      我实际上看到了一个不同的错误,关于类型变量 '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 类型时。

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

      【讨论】:

      • 同意关于保持相互递归类型的评论。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-02-16
      • 2020-04-21
      • 2021-12-04
      • 2010-10-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多